- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我对 Jackson 的多态问题很着迷。
我从事网络 JDR 角色编辑器个人项目。我使用 Springboot 并尝试坚持其哲学。此外,我尝试制作一些独立的包,因为我的实际工作(另一个 springboot 项目)的研究案例。
在没有 Jackson 配置的情况下,我的能力序列化没有问题。但是,当我尝试取回 Web 编辑器上的任何修改时,当 Jackson 对 Competence 进行反序列化时,“依赖”属性就会出现问题。
这是我的类(class):
我尝试序列化/反序列化的:
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(); }}
The superclass of the property I have a problem with:
public interface AttributTemplate extends ComposanteTemplate {}
The two subclasses which could be use for Competence#dependance property:
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; }}
The result json (and then the json I send) is:
{"nom":"Comp_1489746646510","description":"ezbuixnwrclfvmgwdviubcauenzytpzzvumnohwyhpuynxaqhkjdbqygtrmbtlschthovuyoiolkauucwokkfjnaujnufshrjboykuqce","categorie":"APPRENTI","chapitre":"GUERRE","dependance":"ART_ETRANGE","ouverte":false,"sousCompetences":[]}
QUESTION:I understand that my problem is caused by the abstract relation AttributTemplate, and then when Jackson try to deserialize, he does not know which of Carac or ArtTemplate class to use.I try to keep unchanged Competence (Competence come from an external jar), so no annotation on this class is possible.
I've tried many of the solutions I found (Jackson 1.5: Polymorphic Type Handling, first steps ) and the only one which has worked was to define a 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?
<小时/>编辑:我设法做到了:
{"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);
如您所见,我仍然寄生在包装 dependance
值的数组中。我会 (...)"dependance": "AGILE", (...)
而不是 (...)"dependance":["mova.ged.perso.inne. Carac", "AGILE"], (...)
我不知道要改变什么才能得到这个。
最佳答案
我一直在研究你想做什么。不幸的是,我认为枚举+继承存在问题。
我有一个您可以使用的替代解决方案,即使用自定义创建器并忽略未知属性。请参阅以下示例:
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;
}
}
在这里,我将枚举逻辑与 jackson 逻辑分离并实现我自己的逻辑。这不需要自定义序列化。
我基本上是说,我将枚举序列化为其值(您显然可以选择您想要的属性)。
我的输出 json 如下所示:
{"template_type":"TEST_Att1","nome":"nome","desc":"desc"}
在返回步骤中,我现在知道从 template_type
属性构造正确的枚举模板类型所需的信息。这就是我可以注入(inject)到我的工厂方法 create
中的内容。
在创建中,我可以使用静态创建的映射将正确的枚举填充到我的对象中。我们可以静态地创建这个映射,因为我们的枚举是有限的和静态的。
这样做的好处还在于生成器仅用于创建。使用@JsonIgnoreProperties(ignoreUnknown = true),我们可以告诉 Jackson 不要被 json 中的所有自定义元素吓坏。它只会反序列化它可以检测到的任何字段并保留其他字段(因为我们使用自定义 template_type
进行枚举解析)。
最后,我忽略了 bean 中实际的模板
,因为 jackson 无法构造它。
我希望这对您有用/对您有帮助。抱歉耽搁了。
不使用继承的原因:
jackson 中的枚举+继承似乎存在问题。特别是 Jackson 默认使用反射并调用枚举的私有(private)构造函数进行生成。不过,您也许可以让创作者以与上述类似的方式工作。
反序列化需要模板。我假设您不一定想要序列化枚举的所有元素。这是因为枚举名称(在我的例子中 TEST_Att1
)使枚举变得唯一。无需序列化并发送这些枚举所具有的所有不同属性。然而,Deserialization with @JsonSubTypes for no value - missing property error显示 Jackson 要求您的模板字段至少存在。这是一个小问题,因为您想为此使用外部属性(所以为什么要按照 json 中的建议包含一个空字段,只是为了让 jackson 高兴)
这可能不是最好的解决方案,但考虑到限制,我认为它相对优雅。希望对您有帮助,
阿图尔
关于java - Jakson 多态枚举案例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42855646/
我是一名优秀的程序员,十分优秀!