2012-10-28 24 views
2

我試圖運行與澤西的Facebook批處理請求。問題是,Facebook的返回結構的奇怪 - 的JSONObject和JSONString的混合:JsonMappingException當轉換包含嵌套的JSONString的JSONObject

[ 
    { 
     "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 [email protected]annotate.JsonCreator()}] has no property name annotation; must have name when multiple-paramater constructor annotated as Creator 

這個問題的解決似乎是單獨標註每個POJO的屬性而這也正是我說的一點:不,謝謝,當然不是!

所以問題依然存在:有沒有什麼方法可以用ObjectMapper設置反序列化這個混合?

或者,也許我可以告訴澤西過濾傳入的格式化的字符串,並將所有的空白區域發送到傑克遜的反序列化之前切斷?


我不令人滿意的解決辦法:

當我告訴我的BatchResultbodyString,它的作品,我也得到了Stringbody一個BatchResult。現在使用ObjectMapperStringbody再次使用Post.class鍵入正確的反序列化。這是我當前的解決方案,但看起來很亂,我不應該重複反序列化結果來反序列化它的元素......爲什麼傑克遜不能自行解決這個問題呢?

回答

4

剛開身體作爲後

如果你不希望反序列化的身體,那麼你只需要給予郵政對象一個構造函數輸入字符串:

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

解編Body into 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()); 
    } 
} 

然後,你需要一個@JsonDeserialize註釋添加到正文字段,指定解串器的使用方法:

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

您現在應該能夠得到嵌入到JSON您的發佈對象。

+0

謝謝!這聽起來像它可能工作,我會試一試.. – Pete

+0

@Pete我在澤西島以外的地方測試了這個代碼,但我能夠重現您的原始異常,並用此代碼解決它。請注意,我必須在Post類(id,created_time)上定義其他字段。 –