2

我正在尝试使用 Jersey 对 Facebook 运行批处理请求。问题是 Facebook 返回了最奇怪的结构——JSONObject 和 JSONString 的混合:

[
   {
      "code": 200,
      "headers": [
         {
            "name": "Access-Control-Allow-Origin",
            "value": "*"
         },
         <!-- some more headers... -->
      ],
      "body": "{\n   \"message\": \"Hello World!\",\n   \"id\": \"...\",\n   \"created_time\": \"2012-10-17T07:18:02+0000\"\n 
                   <!-- ... -->
               }"
   }
]

现在,当我尝试使用杰克逊ObjectMapper反序列化这个烂摊子时,我得到了一个

JsonMappingException: Can not instantiate value of type [simple type, class package.to.Post] from JSON String; no single-String constructor/factory method (through reference chain: package.to.BatchResult["body"])

这是我正在使用的 POJO 结构:

public class BatchResult<T> {

    private T body;
    private int code;
    private List<BatchResultHeader> headers;
    // ...
}

public class BatchResultHeader {

    private String name;
    private String value;
    // ...
}

public class Post {

    private String message;
    // ...
}

我像这样发送批处理请求。Params 包含文档中定义的批处理参数和批处理请求。还需要批量请求的 POST 调用。所以就像我说的那样,调用应该没问题,因为结果JSON 符合预期(见上文):

Client client = Client.create();
WebResource webResource = client.resource("https://graph.facebook.com");
ClientResponse response = webResource.type("application/x-www-form-urlencoded")
            .post(ClientResponse.class, params);
String json = response.getEntity(String.class);

现在我只使用ObjectMapper序列化:

TypeReference<List<BatchResult<Post>>> ref = new TypeReference<List<BatchResult<Post>>>(){});
ObjectMapper mapper = new ObjectMapper();
List<BatchResult<Post>> batchResults = mapper.readValue(json, ref);

使用@JsonCreator?

因此,当搜索此异常时,我发现了将@JsonCreator注释与Post构造函数一起使用的建议。然而,这导致了

java.lang.IllegalArgumentException: Argument #0 of constructor [constructor for package.to.Post, annotations: {interface org.codehaus.jackson.annotate.JsonCreator=@org.codehaus.jackson.annotate.JsonCreator()}] has no property name annotation; must have name when multiple-paramater constructor annotated as Creator

对此的解决方案似乎是单独注释 POJO 的每个属性,这就是我说的重点:不,谢谢,当然不是!

所以问题仍然存在:有没有办法通过 ObjectMapper 设置反序列化这种混合?

或者我可以告诉 Jersey 过滤传入的格式化字符串并在将其发送给 Jackson 进行反序列化之前剪切所有空格?


我不满意的解决方法:

当我告诉我的BatchResult班级那body是 aString时,它可以工作并且我得到一个带有String body. 现在ObjectMapper再次使用它正确反序列String body化的Post.class类型。这是我当前的解决方案,但它看起来很混乱,我的工作不应该是迭代反序列化结果以反序列化其元素......为什么杰克逊不能自己解决这个问题?

4

1 回答 1

4

只是把身体当作一个帖子

如果您不想反序列化正文,那么您只需要为 Post 对象提供一个采用字符串类型的构造函数:

public class Post {
    public Post( String message ) {
      this.message = message;
    }
    private String message;
    // ...
}

将正文解组为 Post

如果您希望使用字符串中包含的 JSON 填充 Post 对象,则可以使用 JsonDeserializer。首先,您需要定义一个读取字符串值然后解组它的反序列化器。这是一个示例实现:

import java.io.IOException;

import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.ObjectCodec;
import org.codehaus.jackson.map.BeanProperty;
import org.codehaus.jackson.map.ContextualDeserializer;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectReader;

public class EmbeddedJsonDeserializer
  extends JsonDeserializer<Object>
  implements ContextualDeserializer<Object>
{
  Class<?> type = null;
  public EmbeddedJsonDeserializer() {
  }

  public EmbeddedJsonDeserializer( Class<?> type ) {
    this.type = type;
  }

  @Override
  public Object deserialize(JsonParser parser, DeserializationContext context)
      throws IOException, JsonProcessingException {
    JsonToken curr = parser.getCurrentToken();
    if (curr == JsonToken.VALUE_STRING) {
      if( type == null ) return parser.getText();
      ObjectCodec codec = parser.getCodec();
      if( codec == null ) {
        return new ObjectMapper().readValue(parser.getText(), type);
      }
      else if( codec instanceof ObjectMapper ) {
        return ((ObjectMapper)codec).readValue(parser.getText(), type);
      }
      else if( codec instanceof ObjectReader ) {
        return ((ObjectReader)codec).withType(type).readValue(parser.getText());
      }
      else {
        return new ObjectMapper().readValue(parser.getText(), type);
      }
    }
    throw context.mappingException(type);
  }

  public JsonDeserializer<Object> createContextual(DeserializationConfig config, BeanProperty property) throws JsonMappingException {
    return new EmbeddedJsonDeserializer(property.getType().getRawClass());
  }
}

然后需要在body字段中添加@JsonDeserialize注解,指定要使用的反序列化器:

public class BatchResult<T> {
  @JsonDeserialize(using=EmbeddedJsonDeserializer.class)
  private T body;
  ...

您现在应该能够将嵌入的 JSON 嵌入到您的 Post 对象中。

于 2012-12-02T00:02:33.473 回答