我对杰克逊的多态问题很感兴趣。
我在网络JDR角色编辑器个人项目上工作。我使用Springboot并尝试坚持哲学。此外,我尝试制作一些独立的软件包,因为我的实际工作是学习案例(另一个springboot项目)。
没有Jackson配置,我对于能力的序列化没有任何问题。但是当我试图回复网页编辑器的任何修改时,所以当杰克逊对一个能力进行反序列化时,“依赖”属性会出现问题。
以下是我的课程:
我尝试序列化/反序列化的那个:
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 {}
可用于Competence#dependance属性的两个子类:
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
Xcode 7
});
但我对这个解决方案感到很难过,因为我确信还有另一个漂亮的解决方案。
那么可以配置映射器以便能够确定他必须使用哪个Carac或ArtTemplate来获取AttributTemplate吗?
修改 我设法得到了这个:
{"nom":"Comp_1489756873433","description":"kruzueemlwisibshlkotasayfkhdqkqolvhlqgsnntndkpvbmmgklqysabiakaolempmupeyiqaztdcrhwimdksgzybbdzttwnwqjxhfo","categorie":"COMPAGNON","chapitre":"GUERRE","dependance":["mova.ged.perso.inne.Carac","AGILE"],"ouverte":true,"sousCompetences":[...]}
通过配置这样的映射器
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);
正如您所看到的,我仍然使用包含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);
}
值的数组寄生。我会dependance
而不是(...)"dependance": "AGILE", (...)
而且我不知道要改变什么才能有这个。
答案 0 :(得分: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)
,我们可以告诉杰克逊不要被json中的所有自定义元素吓坏。它将简单地反序列化它可以检测到的任何字段并保留其他字段(因为我们使用自定义template_type
进行枚举解析)。
最后,我忽略了我的bean中的实际template
,因为jackson无法构建它。
我希望这适合你/帮助你。抱歉延误。
不使用继承的原因:
杰克逊的enum +继承似乎存在问题。特别是jackson默认使用反射并调用枚举的私有构造函数进行生成。您可以让创作者以与上述类似的方式工作。
反序列化需要模板。我假设您不一定要序列化枚举的所有元素。这是因为枚举名称(在我的情况下为TEST_Att1
)使枚举唯一。无需序列化并发送这些枚举所具有的所有不同属性。但是,Deserialization with @JsonSubTypes for no value - missing property error表示杰克逊要求您的模板字段至少存在。这是一个小问题,因为您想要使用外部属性(所以为什么要包含json中建议的空字段,以使杰克逊高兴)
这可能不是最佳解决方案,但我认为鉴于这些限制,它相对优雅。我希望能帮到你,
Artur