当前位置: 首页 > 知识库问答 >
问题:

Jakson多态枚举事例

朱淮晨
2023-03-14

我遇到了一些jackson多态问题。

我工作在一个web JDR字符编辑器个人项目。我使用springboot并试图坚持哲学。此外,我尝试做一些独立的包,因为学习案例为我的真正工作(另一个springboot项目)。

没有jackson配置,我没有问题序列化一个能力。但是当我试图恢复web编辑器上的任何修改时,所以当jackson对一个能力进行反序列化时,“dependance”属性就会出现问题。

以下是我的类:

我尝试序列化/反序列化的一个:

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引起的,然后当jackson尝试反序列化时,他不知道该使用Carac或ArtTemplate类中的哪一个。我尝试保持不变的能力(能力来自外部jar),因此不可能对这个类进行注释。

我已经尝试了我找到的许多解决方案(jackson 1.5:多态类型处理,第一步),唯一奏效的是定义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”],(...)
而且我不知道要改变什么才能得到这个。

共有1个答案

章丰茂
2023-03-14

我一直在调查你想做什么。不幸的是,我认为枚举+继承存在问题。

我有一个替代的解决方案,您可以使用,它是使用一个自定义的创建者,并忽略未知的属性。请参阅以下示例:

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属性构造正确的枚举模板类型所需的信息。这是我可以注入到我的工厂方法create中的内容。

在创建中,我可以使用静态创建的映射将正确的枚举填充到对象中。我们可以静态地创建这个映射,因为枚举是有限的和静态的。

这其中的妙处还在于,生成器只用于创作。使用@jsonIgnoreProperties(ignoreUnknown=true),我们可以告诉jackson不要被JSON中的所有自定义元素吓坏。它将简单地反序列化它可以检测到的所有字段,并保留其他字段(因为我们使用自定义的template_type解析枚举)。

最后,我忽略了bean中实际的template,因为jackson无法构造它。

我希望这对你有用/有帮助。抱歉耽搁了。

不使用继承的原因:

>

  • 在jackson,枚举+继承似乎有问题。特别是jackson,默认情况下使用反射并调用枚举的私有构造函数进行生成。但是,您可能能够让创建者以与上面类似的方式工作。

    反序列化需要模板。我的假设是,您不一定要序列化枚举的所有元素。这是因为枚举名称(在我的示例中为test_att1)使枚举唯一。不需要序列化和发送这些枚举周围的所有不同属性。但是,对于no value-missing property错误,使用@JSONSubtypes进行反序列化显示,jackson要求模板字段至少存在。这是一个小问题,因为您希望使用外部属性来代替它(所以为什么要像json中建议的那样包含一个空字段,只是为了让jackson高兴)

    这可能不是最好的解决方案,但我认为考虑到限制,这是比较优雅的。我希望这对你有帮助,

    阿图尔

  •  类似资料:
    • 问题内容: Java中的静态和非静态枚举有什么区别?两种用法是相同的。 正确吗 所有静态的都在启动时加载到内存中,非静态的则按需加载 ? 如果是,那么哪种方法更好?将某些数据始终保留在内存中还是每次使用服务器资源加载它们? 问题答案: 所有的都是有效的。如果您有嵌套的枚举,则它与相同。 所有类都是延迟加载的(枚举或其他),但是在加载时,它们会一次全部加载。也就是说,您不能加载一些常量,而不能加载其

    • 问题内容: 假设我有一个格式为基本XML的文件,如下所示: 我想在运行时变成这样的东西: …,然后将新创建的枚举传递给我的应用程序。我将如何实现这样的目标?可以吗 问题答案: 您尝试做的事情没有任何意义。枚举实际上仅是为了编译时的利益,因为它们表示一组固定的常量。在运行时,动态生成的枚举的含义是什么- 与普通对象有什么不同?例如: 您的XML可以解析为新实例化的对象,这些对象可以存储在某些程序中,

    • 问题内容: 这不是卡住我的问题,而是我正在寻找一种编写代码的整洁方法。 本质上,我正在编写一个事件驱动的应用程序。用户触发一个事件,该事件被发送到适当的对象,然后这些对象处理事件。现在,我正在编写偶数处理程序方法,并且希望使用switch语句确定如何处理事件。现在,在我研究通用结构时,事件类非常简单: 然后,在另一堂课中,我会看到类似以下内容的内容: 我会 喜欢 做的就是这样的事情(尽管我当然会坚

    • 我正在使用JacksonPolymorphicDeserialization,这是我的代码,它反序列化到基于“type”属性的适当类中: 它工作得很好,我的json根据“type”值变成了预期的类。 但是,我正在考虑将“type”属性从String移动到Enum,这是我的新代码,带有以下更改: 和枚举: 问题是,第二种方法不起作用。。。知道为什么吗???我可以在这里使用Enum吗??? 谢谢!

    • 问题内容: 我有几个需要国际化的枚举(我需要将枚举值转换为某些语言才能在jsf页面中显示)。考试列举: 翻译将是例如/ 转换应存储在MessageBundle(属性文件)中。我正在寻找一个简单,通用的解决方案(最好是无需在所有枚举中编写额外的代码),而该解决方案在jsf方面并不需要太多。仅提及它,当然,两个不同的枚举可能具有相同的枚举值(例如,类似的值在不同的枚举中具有不同的含义)。 我想出的解决

    • 问题内容: 我在Android应用程序中使用了几个基于枚举的状态机。尽管这些方法工作得很好,但我正在寻找一个建议,以建议如何优雅地将事件(通常是从已注册的回调或eventbus消息中)接收到当前活动状态。在许多有关基于枚举的FSM的博客和教程中,大多数都提供了消耗数据(例如解析器)的状态机示例,而不是说明如何从事件中驱动这些FSM。 我正在使用的典型状态机具有以下形式: 在我的情况下,某些状态会触