2017-03-17 65 views
0

我被一些傑克遜多態問題困住了。Jakson多態枚舉case

我在Web JDR Character Editor個人項目上工作。我使用Springboot並試圖堅持哲學。此外,我嘗試製作一些獨立的軟件包,因爲我的實際工作(另一個springboot項目)的研究案例。

沒有傑克遜配置,我沒有問題的序列化能力。但是當我試圖取回網頁編輯器的任何修改時,所以當傑克遜對權限進行反序列化時,「依賴」屬性會出現問題。

這裏是我的課:

一個我試圖序列化/解:

public class Competence implements Composante, ComposanteTemplate { 

    public enum Categorie { 
     APPRENTI, 
     COMPAGNON 
    } 

    private String nom; 
    private String description; 
    private Categorie categorie; 
    private Chapitre chapitre; 
    private AttributTemplate dependance; 
    private List sousCompetences = new ArrayList(); 

    public String getNom() { 
     return nom; 
    } 

    public void setNom(String nom) { 
     this.nom = nom; 
    } 

    public String getDescription() { 
     return description; 
    } 

    public void setDescription(String description) { 
     this.description = description; 
    } 

    public Competence getTemplate() { 
     return this; 
    } 

    public Categorie getCategorie() { 
     return categorie; 
    } 

    public void setCategorie(Categorie categorie) { 
     this.categorie = categorie; 
    } 

    public Chapitre getChapitre() { 
     return chapitre; 
    } 

    public void setChapitre(Chapitre chapitre) { 
     this.chapitre = chapitre; 
    } 

    public AttributTemplate getDependance() { 
     return dependance; 
    } 

    public void setDependance(AttributTemplate dependance) { 
     this.dependance = dependance; 
    } 

    public List getSousCompetences() { 
     return sousCompetences; 
    } 

    public void setSousCompetences(List sousCompetences) { 
     this.sousCompetences = sousCompetences; 
    } 

    public boolean isOuverte() { 
     return !sousCompetences.isEmpty(); 
    } 
}

屬性的超我有一個問題:

public interface AttributTemplate extends ComposanteTemplate {}

兩個子類可用於能力#依賴屬性:

public enum Carac implements AttributTemplate, Attribut { 

    FORT(Type.PHYSIQUE), 
    AGILE(Type.PHYSIQUE), 
    RESISTANT(Type.PHYSIQUE), 
    OBSERVATEUR(Type.PHYSIQUE), 
    SAVANT(Type.MENTALE), 
    RUSE(Type.MENTALE), 
    TALENTUEUX(Type.MENTALE), 
    CHARMEUR(Type.MENTALE); 

    public enum Type { 
     PHYSIQUE, 
     MENTALE 
    } 

    public final Type type; 
    public final String nom = name().toLowerCase(); 

    private String description; 

    Carac(Type type) { 
     this.type = type; 
    } 

    @Override 
    public String getNom() { return nom; } 

    @Override 
    public String getDescription() { return description; } 

    @Override 
    public Carac getTemplate() { return this; } 

    public void setDescription(String description) { this.description = description; } 

}
public enum ArtTemplate implements AttributTemplate { 

    ART_GUERRIER(2, 1), 
    ART_ETRANGE(1, 2), 
    ART_GUILDIEN(1, 1); 

    public static final String ART_PREFIX = "ART"; 

    public final String nom = name().toLowerCase().replace("_", " "); 
    public final int nbCaracsPhysiques; 
    public final int nbCaracsMentales; 

    private String description; 

    ArtTemplate(int nbCaracsPhysiques, int nbCaracsMentales) { 
     this.nbCaracsMentales = nbCaracsMentales; 
     this.nbCaracsPhysiques = nbCaracsPhysiques; 
    } 

    @Override 
    public String getNom() { 
     return nom; 
    } 

    @Override 
    public String getDescription() { 
     return description; 
    } 

    public void setDescription(String description) { 
     this.description = description; 
    } 

    public int getNbCaracs() { 
     return nbCaracsPhysiques + nbCaracsMentales; 
    } 

}

結果JSON(然後我發送JSON)是:

{"nom":"Comp_1489746646510","description":"ezbuixnwrclfvmgwdviubcauenzytpzzvumnohwyhpuynxaqhkjdbqygtrmbtlschthovuyoiolkauucwokkfjnaujnufshrjboykuqce","categorie":"APPRENTI","chapitre":"GUERRE","dependance":"ART_ETRANGE","ouverte":false,"sousCompetences":[]}

問題: 據我所知,我的問題是由抽象的關係AttributTemplate引起的,然後當傑克遜嘗試反序列化,他不知道要使用哪個Carac或ArtTemplate類。 我試圖保持不變能力(能力來自外部的jar),所以這個類沒有註釋是可能的。

我試過很多我找到的解決(Jackson 1.5: Polymorphic Type Handling, first steps),只有已工作的一個是定義DeserializationProblemHandler

mapper.addHandler(new DeserializationProblemHandler() { 
@Override 
public Object handleMissingInstantiator(DeserializationContext ctxt, Class<?> instClass, JsonParser p, String msg) throws IOException { 
    if (instClass == AttributTemplate.class) { 
     String name = p.getText(); 
     return !name.startsWith(ArtTemplate.ART_PREFIX) ? Carac.valueOf(name) : ArtTemplate.valueOf(name); 
    } 
    return super.handleMissingInstantiator(ctxt, instClass, p, msg); 
} 

});

但是我對這個解決方案感到不滿,因爲我確信還有另外一個漂亮的解決方案。

那麼是否可以配置映射器,以便他能夠確定他必須使用哪個Carac或ArtTemplate才能獲得AttributTemplate?


編輯: 我設法有這樣的:通過這樣的配置映射器

 
    abstract class CompetenceMixIn { 

     private AttributTemplate dependance; 

     @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.EXISTING_PROPERTY, property="dependance") 
     @JsonSubTypes({ @JsonSubTypes.Type(value = Carac.class, name = "carac"), @JsonSubTypes.Type(value = ArtTemplate.class, name = "artTemplate") }) 
     public void setDependance(AttributTemplate dependance) { 
      this.dependance = dependance; 
     } 
    }
ObjectMapper mapper = jsonConverter.getObjectMapper(); 
mapper.addMixIn(Competence.class, CompetenceMixIn.class);

{"nom":"Comp_1489756873433","description":"kruzueemlwisibshlkotasayfkhdqkqolvhlqgsnntndkpvbmmgklqysabiakaolempmupeyiqaztdcrhwimdksgzybbdzttwnwqjxhfo","categorie":"COMPAGNON","chapitre":"GUERRE","dependance":["mova.ged.perso.inne.Carac","AGILE"],"ouverte":true,"sousCompetences":[...]}

正如你可以看到,我仍然與包裹的陣列一起寄生值。我會(...)"dependance": "AGILE", (...)不是(...)"dependance":["mova.ged.perso.inne.Carac", "AGILE"], (...)
而我不知道要改變這個。

+0

我已經回答過類似的問題。傑克遜允許這是可配置的。我不想爲你的具體情況重塑這個,所以請看看這裏:http://stackoverflow.com/questions/38501574/rules-for-jersey-to-parse-json-jackson-subtype-deserialisation/ 38523801#38523801這裏的例子非常簡單。 Params類有2個實現,我告訴傑克遜什麼屬性來查找如何反序列化它 – pandaadb

+0

我嘗試你的例子,但仍然無法找到真正的解決方案。我通過解決方案的開始修改了我的帖子,我感謝你。 – Mohicane

+0

仍然有問題....我無法想象有這樣的問題,對我來說這麼簡單的事情? – Mohicane

回答

1

我一直在尋找你想要做的事情。不幸的是,我認爲Enums +繼承存在問題。

我有一個替代解決方案,您可以使用哪個是使用自定義創建者並忽略未知屬性。請看下面的例子:

public class JacksonInheritance { 

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

     Competence c = new Competence(); 
     c.desc = "desc"; 
     c.nome = "nome"; 
     c.template = Att1.TEST_Att1; 
     String test = mapper.writeValueAsString(c); 
     System.out.println(test); 

     Competence readValue = mapper.readValue(test, Competence.class); 
     System.out.println(readValue.template); 
    } 

    @JsonIgnoreProperties(ignoreUnknown = true) 
    public static class Competence { 

     private static final Map<String, AttributeTemplate> templates; 
     static { 
      templates = new HashMap<>(); 
      Stream.of(Att1.values()).forEach(a -> templates.put(a.name(), a)); 
      Stream.of(Att2.values()).forEach(a -> templates.put(a.name(), a)); 
     } 

     @JsonProperty 
     String nome; 
     @JsonProperty 
     String desc; 
     @JsonIgnore 
     AttributeTemplate template; 

     @JsonProperty("template_type") 
     public String getTempl() { 
      // Here you can do whichever way uou would like to serialise your template. This will be the key 
      return template.toString(); 
     } 

     @JsonCreator 
     public static Competence create(@JsonProperty("template_type") String templateType) { 
      Competence c = new Competence(); 
      c.template = templates.get(templateType); 
      return c; 
     } 
    } 

    public static interface AttributeTemplate { 
    } 

    public static enum Att1 implements AttributeTemplate { 
     TEST_Att1; 
    } 

    public static enum Att2 implements AttributeTemplate { 

     TEST2_Att2; 
    } 
} 

在這裏,我從傑克遜邏輯分離枚舉的邏輯和實現我自己的。這不需要自定義序列化。

我基本上說,我連載我的枚舉作爲其值(可以很明顯的選擇,你想爲這裏面永遠屬性)。然後

我的輸出JSON看起來:

{"template_type":"TEST_Att1","nome":"nome","desc":"desc"} 

在返回步我現在知道了信息,我需要建立從template_type屬性的正確枚舉模板類型。這是我可以注入我的工廠方法create

在創建中,我可以使用靜態創建的映射將正確的枚舉填充到我的對象中。由於我們的枚舉是有限且靜態的,因此我們可以靜態創建此映射。

這也是美的發電機只用於創造。使用@JsonIgnoreProperties(ignoreUnknown = true),我們可以告訴jackson不要被json中的所有自定義元素嚇倒。它將簡單地反序列化它可以檢測到的任何字段並保留其他字段(因爲我們使用自定義的template_type來枚舉分辨率)。

最後,我在我的豆無視實際template因爲傑克遜將無法構建。

我希望這對你有用/幫助你。對不起,延遲。

理由不使用繼承:

  1. 似乎有與枚舉+繼承傑克遜的問題。特別是傑克遜默認使用反射並調用枚舉的私有構造函數進行生成。儘管如此,您也許可以讓創作者以類似的方式工作。

  2. 反序列化期望模板。我假設你不一定要序列化枚舉的所有元素。這是因爲枚舉名稱,在我的案例TEST_Att1使枚舉唯一。沒有必要序列化併發送這些枚舉所具有的所有不同屬性。但是,Deserialization with @JsonSubTypes for no value - missing property error顯示傑克遜需要您的模板字段至少存在。這是AA輕微的問題,因爲要使用外部屬性用於此,而不是(所以爲什麼包括一個空場作爲您的JSON建議只是爲了讓傑克遜高興)

這可能不是最好的解決辦法,但考慮到這些限制,我認爲它相對優雅。我希望能幫到你,

Artur

+0

如果您無法更改競爭類,則可以對其進行擴展並在該類中添加jsoncreator。如果這不是一個選項,我認爲你的解決方案最初提供的是非常好的 – pandaadb

+0

我會嘗試你的選擇,但我有點壟斷我的工作:) – Mohicane

+0

我終於(畢竟這一次!)嘗試你的解決方案。正如你所說,我無法修改Competence類。然後我使用MixIn的可能性來添加註釋和創建者。但似乎創作者並沒有以這種方式使用。 – Mohicane