2014-12-03 40 views
3

我目前正在嘗試開發與某個在線服務進行通信的REST客戶端。這個在線服務返回一些我希望映射到Java對象的JSON響應,使用Jackson將JSON映射爲具有單屬性和多屬性的多態POJO

JSON響應的一個例子是:

{ 
    "id" : 1, 
    "fields" : [ { 
     "type" : "anniversary", 
     "value" : { 
     "day" : 1, 
     "month" : 1, 
     "year" : 1970 
     } 
    }, { 
     "type" : "birthday", 
     "value" : { 
     "day" : 1, 
     "month" : 1, 
     "year" : 1970 
     } 
    }, { 
     "type" : "simple", 
     "value" : "simple string" 
    },{ 
     "type": "name", 
     "value": { 
      "firstName": "Joe", 
      "lastName": "Brown" 
     } 
    } ] 
} 

注意以下幾點:

  • 該結構包含一個簡單的ID,和場實例的集合,每一個都具有類型
  • 結構由外部公式確定operty
  • 在給出的例子中
  • ,有3種類型 - >日期,名稱,及單值串
  • 生日週年類型二者匹配日期結構
  • 有幾類映射到一個字符串值,如電子郵件類型,twitterId類型,公司型等

我的問題是,我似乎並沒有能夠正確地映射這種結構的Java對象

這是我到目前爲止已經完成。以下是課程和他們的傑克遜註釋(吸氣劑和吸附劑被省略)。

public class Contact { 
    private int id; 
    private List<Field> fields; 
} 

public class Field { 
    private FieldType type; 
    private FieldValue value; 
} 

public enum FieldType { 
    EMAIL, NICKNAME, NAME, ADDRESS, BIRTHDAY, ANNIVERSARY 
} 

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type", 
     defaultImpl = SingleFieldValue.class) 
@JsonSubTypes({ 
     @JsonSubTypes.Type(value = NameFieldValue.class, name = "name"), 
     @JsonSubTypes.Type(value = DateFieldValue.class, name = "anniversary"), 
     @JsonSubTypes.Type(value = DateFieldValue.class, name = "birthday"), 
     @JsonSubTypes.Type(value = SingleFieldValue.class, name = "nickname"), 
     @JsonSubTypes.Type(value = SingleFieldValue.class, name = "email"), 
     //other types that map to SingleFieldValue 
}) 
public abstract FieldValue { 
} 


public class NameFieldValue extends FieldValue { 

    private String firstName; 
    private String lastName; 
} 

public class DateFieldValue extends FieldValue { 

    private int day; 
    private int month; 
    private int year; 
} 

public class SingleFieldValue extends FieldValue { 

    private String value; 
} 

ObjectMapper不包含任何配置,則使用默認配置。

你有什麼建議要正確映射這些?我想避免製作自定義的反序列化器,而只是遍歷Json對象,比如JsonNode。

備註:對於缺乏足夠明確的信息,我提前道歉。請說明我的配方有任何問題。

+0

你有沒有考慮過簡單地寫一些軟件來做到這一點? – 2014-12-03 13:05:35

+0

我的願望是使用傑克遜解開JSON。我想這就是你所指的。 – 2014-12-03 13:12:14

+0

將'value'作爲Map並對其進行排序。你可以離開休息。 – 2014-12-03 13:13:36

回答

2

您已經使用FieldValue級別的抽象類在FIeld類中使用它。在這種情況下,您可以構造類型爲= email和value = address的對象,這可能會導致一些問題...

我建議爲具有特定FieldValue類型的每種類型創建特定的類。從/ 下面的代碼序列化/反序列化JSON所需的格式從/到POJO:

public class Main { 
    String json = "{\"id\":1,\"fields\":[{\"type\":\"SIMPLE\",\"value\":\"Simple Value\"},{\"type\":\"NAME\",\"value\":{\"firstName\":\"first name\",\"lastName\":\"last name\"}}]}"; 

    public static void main(String []args) throws IOException { 
     ObjectMapper objectMapper = new ObjectMapper(); 

     String json = objectMapper.writeValueAsString(generate()); 
     System.out.println(json); 

     System.out.println(objectMapper.readValue(json, Contact.class)); 
    } 

    private static Contact generate() { 
     SimpleField simpleField = SimpleField.builder().type(FieldType.SIMPLE).value("Simple Value").build(); 

     NameFieldValue nameFieldValue = NameFieldValue.builder().firstName("first name").lastName("last name").build(); 
     NameField nameField = NameField.builder().type(FieldType.NAME).value(nameFieldValue).build(); 

     return Contact.builder().id(1).fields(Arrays.asList(simpleField, nameField)).build(); 
    } 
} 

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type") 
@JsonSubTypes({ 
     @JsonSubTypes.Type(value = SimpleField.class, name = "SIMPLE"), 
     @JsonSubTypes.Type(value = NameField.class, name = "NAME") 
}) 
interface Field { 
    FieldType getType(); 
    Object getValue(); 
} 

enum FieldType { 
    SIMPLE, NAME 
} 

@Data 
@Builder 
@AllArgsConstructor 
@NoArgsConstructor 
class Contact { 
    private int id; 
    private List<Field> fields; 
} 

@Data 
@Builder 
@AllArgsConstructor 
@NoArgsConstructor 
class SimpleField implements Field { 
    private FieldType type; 
    private String value; 

    @Override 
    public FieldType getType() { 
     return this.type; 
    } 

    @Override 
    public String getValue() { 
     return this.value; 
    } 
} 

@Data 
@Builder 
@AllArgsConstructor 
@NoArgsConstructor 
class NameField implements Field { 
    private FieldType type; 
    private NameFieldValue value; 

    @Override 
    public FieldType getType() { 
     return this.type; 
    } 

    @Override 
    public Object getValue() { 
     return this.value; 
    } 
} 

@Data 
@Builder 
@AllArgsConstructor 
@NoArgsConstructor 
class NameFieldValue { 
    private String firstName; 
    private String lastName; 
} 

我用龍目島庫這裏只是爲了儘量減少代碼,避免創建getter/setter方法以及構造函數。您可以刪除lombok註釋並添加getters/setters /構造函數,並且代碼的工作原理相同。

所以,這個想法是,你有一個Contact類(這是你的JSON的根)與一個字段列表(其中字段是一個接口)。每個Field類型都有自己的實現,如NameField實現Field並將NameFieldValue作爲屬性。這裏的技巧是你可以改變getValue()方法聲明並聲明它返回通用接口或對象(我使用Object但接口也可以)。

該解決方案不需要任何定製串行器/解串器,並且易於維護。

+0

對不起,等待。但這確實幫助我達成了一個解決方案。有一點需要注意,在這種情況下,include的值現在是JsonTypeInfo.As.PROPERTY,因爲它與Field對象處於同一級別。非常感謝你! – 2014-12-08 18:18:13