2013-01-02 51 views
1

我打一個RESTful的第三方API,它總是發送JSON的格式如下:配置XStream的動態映射到不同的對象

{ 
    "response": { 
     ... 
    } 
} 

哪裏...是響應對象需要被映射回一個Java POJO。例如,有時JSON將包含應該被映射回Fruit POJO數據:

{ 
    "response": { 
     "type": "orange", 
     "shape": "round" 
    } 
} 

...有時JSON將包含應被映射回一個Employee POJO數據:

{ 
    "response": { 
     "name": "John Smith", 
     "employee_ID": "12345", 
     "isSupervisor": "true", 
     "jobTitle": "Chief Burninator" 
    } 
} 

所以取決於REST的API調用,我們需要這兩個JSON結果映射回兩者之一:

public class Fruit { 
    private String type; 
    private String shape; 

    // Getters & setters for all properties 
} 

public class Employee { 
    private String name; 
    private Integer employeeId; 
    private Boolean isSupervisor; 
    private String jobTitle; 

    // Getters & setters for all properties 
} 

不幸的是,我不能昌e此第三方REST服務總是發回一個{ "response": { ... } } JSON結果。但我仍然需要一種方式來配置映射到動態映射這樣的response回無論是FruitEmployee

首先,我想Jackson了有限的成功,但我想這是它不是作爲配置。所以現在我試圖用XStream及其JettisonMappedXmlDriver將JSON映射回POJO。這裏的原型代碼我有:

public static void main(String[] args) { 
    XStream xs = new XStream(new JettisonMappedXmlDriver()); 

    xs.alias("response", Fruit.class); 
    xs.alias("response", Employee.class); 

    // When XStream sees "employee_ID" in the JSON, replace it with 
    // "employeeID" to match the field on the POJO. 
    xs.aliasField("employeeID", Employee.class, "employee_ID"); 

    // Hits 3rd party RESTful API and returns the "*fruit version*" of the JSON. 
    String json = externalService.getFruit(); 

    Fruit fruit = (Fruit)xs.fromXML(json); 
} 

不幸的是,當我運行此我得到一個例外,因爲我有xs.alias("response", ...)映射response到2個不同的Java對象:

Caused by: com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$UnknownFieldException: No such field me.myorg.myapp.domain.Employee.type 
---- Debugging information ---- 
field    : type 
class    : me.myorg.myapp.domain.Employee 
required-type  : me.myorg.myapp.domain.Employee 
converter-type  : com.thoughtworks.xstream.converters.reflection.ReflectionConverter 
path    : /response/type 
line number   : -1 
version    : null 
------------------------------- 

於是我問:什麼都可以我是否會繞過這樣的事實,即API將始終發送相同的「包裝器」response JSON對象?我能想到的唯一的事情就是首先做一個像這樣的字符串替換:

String json = externalService.getFruit(); 
json = json.replaceAll("response", "fruit"); 
... 

但是,這似乎是一個醜陋的黑客。 XStream(或其他映射框架)是否提供了可以幫助我解決這個特定情況的任何事情? Thansk提前。

回答

0

有兩種方式與傑克遜:

  • 測試手動使想要鍵存在(JsonNode有必要的方法);
  • 使用JSON模式; Java中有一個API:json-schema-validator(是的,那是我的),它使用了Jackson。

寫模式匹配你的第一個對象類型:

{ 
    "type": "object", 
    "properties": { 
     "type": { 
      "type": "string", 
      "required": true 
     }, 
     "shape": { 
      "type": "string", 
      "required": true 
     } 
    }, 
    "additionalProperties": false 
} 

負載以此爲模式,驗證您輸入反對:如果驗證,你知道你需要反序列化對你的水果類。否則,爲第二個項目類型制定模式,對其進行驗證以作爲安全措施,並使用其他類進行反序列化。

code examples for the API,太(1.4版本。x)

0

如果你確實知道實際的類型,那麼Jackson應該是相對直接的。 你需要使用一個通用的包裝類型,如:

public class Wrapper<T> { 
    public T response; 
} 

,然後唯一的訣竅是構造類型的對象,讓傑克遜知道什麼T有。 如果是靜態可用,你只是做:

Wrapper<Fruit> wrapped = mapper.readValue(input, new TypeReference<Wrapper<Fruit>>() { }); 
Fruit fruit = wrapped.response; 

但如果是多個動態生成的,是這樣的:

Class<?> rawType = ... ; // determined using whatever logic is needed 
JavaType actualType = mapper.getTypeFactory().constructGenericType(Wrapper.class, rawType); 
Wrapper<?> wrapper = mapper.readValue(input, actualType); 
Object value = wrapper.response; 

但無論哪種方式,「應該只是工作」。請注意,在後一種情況下,您可以使用基本類型(「?extends MyBaseType」),但通常不能指定動態類型。

相關問題