2013-01-02 8 views
1
난 항상 다음과 같은 형식으로 JSON을 보내는 편안하고 제 3 자 API 타격하고

:구성 XStream을 동적으로 다른 개체에 매핑하는

{ 
    "response": { 
     ... 
    } 
} 

... 자바로 다시 매핑 할 필요가있는 응답 객체입니다 POJO. 예를 들어, 때로는 JSON은 Fruit POJO로 다시 매핑해야 데이터가 포함됩니다

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

을 ... 때로는 JSON은 Employee POJO로 다시 매핑해야 데이터가 포함됩니다

{ 
    "response": { 
     "name": "John Smith", 
     "employee_ID": "12345", 
     "isSupervisor": "true", 
     "jobTitle": "Chief Burninator" 
    } 
} 
을 나는 장 수없는, 불행하게도

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 
} 

:

그래서 편안하고 API 호출에 따라, 우리는이 두 가지 JSON 결과는 두 가지 중 하나에 다시 매핑해야합니다 e 제 3 자 REST 서비스가 항상 { "response": { ... } } JSON 결과를 반환한다는 사실. 그러나 나는 여전히 을 Fruit 또는 Employee 중 하나로 동적으로 맵핑하도록 매퍼를 구성하는 방법이 필요하다.

처음에는 제한된 성공으로 Jackson을 시도했지만, 원하는대로 구성 할 수 없었습니다. 그래서 지금 XStreamJettisonMappedXmlDriver과 함께 사용하여 JSON을 POJO로 다시 매핑하려고합니다. 내가 xs.alias("response", ...) 매핑 response이 서로 다른 자바 객체가 있기 때문에, 나는 예외가이 프로그램을 실행할 때 불행하게도

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); 
} 

: 여기에 내가 가지고있는 프로토 타입 코드의

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가 항상 동일한 "wrapper"response JSON 객체를 돌려 보낼 것이라는 사실을 회피하려고합니까?

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

을하지만이 못생긴 해킹 것 같아 : 내가 생각할 수있는 유일한 것은 먼저하고있다과 같이 문자열을-교체하십시오. XStream (또는 다른 매핑 프레임 워크)이이 특별한 경우에 나를 도울만한 것을 제공합니까? 미리 Thansk.

답변

0

방법은 두 가지가 잭슨이 있습니다

  • 테스트 수동으로 원하는 키 (JsonNode 필요한 방법이있다)이 있다는 것을;
  • JSON 스키마 사용; 자바에는 하나의 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")을 사용할 수 있지만 일반적으로 동적 유형을 지정할 수 없습니다.