Resteasy PUT 或 POST with Json Object 导致 UnrecognizedPropertyException

Resteasy PUT or POST with Json Object causes UnrecognizedPropertyException

这是关于一个带有 JSON 对象和根元素的 REST PUT 调用,比方说,类似于 :

{"place":
    {"name":"Here",
     "address":
         {"address":"somewhere under the rainbow",
          "city":
                 {"name":"Oz",
                  "country":
                    {"name":"Oz",
                     "zone":"Far Far Away"}
                  }
          }
     } 
}

使用 Jaxb 注释定义

@XmlRootElement
public class PlaceOffline extends GlobalElement {
     @XmlElement
     public Address address;
}

超类是

@XmlRootElement
public class GlobalElement implements Serializable {
     @XmlElement
     public Integer id;
     @XmlElement
     public String name;
     @XmlElement
     public String uniqueLabel;
}

我写了一个客户端,它悲惨地失败了

 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "placeOffline"

然后我决定记录调用并创建一个 ContainerRequestFilter 来执行此操作

@Provider
public class RestLogFilter implements ContainerRequestFilter {
    private Logger LOGGER_filter = Logger.getLogger(RestLogFilter.class.getName());

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        if (!MediaType.APPLICATION_JSON_TYPE.equals(requestContext.getMediaType())
            || requestContext.getEntityStream() == null) {
            return;
        }
    InputStream inputStream = requestContext.getEntityStream();
    byte[] bytes = IOUtils.readFully(inputStream , -1, true);
    LOGGER_filter.info("Posted: " + new String(bytes, "UTF-8"));
    requestContext.getEntityStream().mark(0);
}

}

而且,你猜怎么着?

出乎意料的成功了!没有更多的错误。实体的打印解决了这个问题。尴尬。

我认为它是一种解决方法,但它让我有点困扰,有人可以解释一下吗?解决这个问题的更优雅的方法?

我正在使用 WildFly 8.2

编辑:

它与

一起工作
public class ObjectMapperContextResolver implements     ContextResolver<ObjectMapper> {
    private final ObjectMapper mapper;

    public ObjectMapperContextResolver() {
        mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return mapper;
    }
}

您需要将 Jackson 添加到应用程序中(只是为了编译),因为您将需要其中的一些 classes。 Wildfly 使用 Jackson 反序列化 JSON,默认行为是当它看到 JSON 上未在 POJO 中建模的属性时失败。例如下面的 JSON

{"firstName": "stack", "lastName": "overflow"}

有一个 属性 "lastName" 不在下面的 POJO

public class Person {
    private String firstName;
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getFirstName() { return firstName; }
}

所以它会因 Jackson 的默认行为而失败。

对于依赖,可以添加

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jackson2-provider</artifactId>
    <version>3.0.9.Final</version>
    <scope>provided</scope>
</dependency>

请注意 provided 范围,因为 Wildfly 已经具有此依赖项。如果您不使用 Maven,那么您真正需要的只是 jackson-databindjacskon-annotations。只需抓住 2.4.x 版本。如果您使用 Jars,请务必不要将这些 jar 与您的 war 打包在一起,因为版本可能与 Wildfly 已有的冲突。

现在,如果您只想忽略这个 class 的未知属性,您可以只为 class

添加一个注释
@JsonIgnoreProperties(ignoreUnknown = true)
public class PlaceOffline extends GlobalElement {

或者如果你想全局配置这个行为,你可以看ObjectMapper的配置方法如下

ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

更新

如果失败是由于 JSON 被包装,例如

{"person": {"firstName": "stack", "lastName": "overflow"}}

然后你需要设置属性

mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);

这样做是在 class 上注释的 @XmlRootElement(name="person")@JsonRootName("person") 中查找 class 名称的值。对于 JAXB 注释支持,您仍然需要配置 JAXB 注释模块

mapper.registerModule(new JaxbAnnotationModule());

此模块包含在 jackson-module-jaxb-annotations jar 中。如果您使用 Maven(具有上述依赖项),它已经被引入。