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 
                   <!-- ... -->
               }"
   }
]

Jackson を使用して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と伝えるとString、それは機能し、 . で BatchResult を取得しString bodyます。正しく逆シリアル化するタイプでObjectMapperもう一度使用します。これは私の現在の解決策ですが、見た目が乱雑で、デシリアライゼーションの結果を反復してその要素をデシリアライズするのは私の仕事ではありません...なぜジャクソンはこれを自分で理解できないのですか?String bodyPost.class

4

1 に答える 1

4

体をポストとして取得するだけ

本文の逆シリアル化を検討していない場合は、Postオブジェクトに文字列型をとるコンストラクターを指定する必要があります。

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

ボディをポストにアンマーシャリングする

代わりに、文字列に含まれる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());
  }
}

次に、@ JsonDeserializeアノテーションをbodyフィールドに追加し、使用するデシリアライザーを指定する必要があります。

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

これで、Postオブジェクトに埋め込まれたJSONを取得できるようになります。

于 2012-12-02T00:02:33.473 に答える