根据父属性反序列化 JSON 子类型

huangapple go评论89阅读模式
英文:

Deserializing json subtype based on parent property

问题

我有一个带有动态attribute子项的 JSON,如下所示:

{  
  "label":"Some label",
  "attribute": {     
    "type": "TEXT",  
    "languages":[
      {
          "language":"en_EN",
          "text":"do you approve?"
      }
    ]
  }
}

或者

{  
  "label":"Some label",
  "attribute": {
    "type": "NUMBER",
    "value": "10.0"
  }
}

我能够使用以下代码正确反序列化上述 JSON,其中使用了@JsonSubTypes

@Data
public class Field {
    private String label;
    private Attribute attribute;
}

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = Attribute.NumberAttribute.class, name = "NUMBER"),
    @JsonSubTypes.Type(value = Attribute.TextAttribute.class, name = "TEXT")
})
public class Attribute {
    private AttributeType type;

    @Data
    public static class TextAttribute extends Attribute {
        List<Language> languages;
    }

    @Data
    public static class NumberAttribute extends Attribute {
        String value;
    }

    @Data
    public static class Language {
        private String text;
        private String language;
    }
}

然而,我遇到的问题是,我必须使用 attribute 对象内部的 type 字段,我希望将 type 移到父对象中。最终的 JSON 应该像这样:

{  
  "type": "TEXT",  
  "label":"Some label",
  "attribute": {   
    "languages":[
      {
          "language":"en_EN",
          "text":"do you approve?"
      }
    ]
  }
}

或者

{  
  "type": "NUMBER",
  "label":"Some label",
  "attribute": {
    "value": "10.0"
  }
}

我找不到任何方法来使用父字段(或 JSON 路径方式)来使用位于动态子类型之外的 type 属性。你知道我如何做到这一点吗?

英文:

I have a json coming with a dynamic attribute child, like below:

{  
  &quot;label&quot;:&quot;Some label&quot;,
  &quot;attribute&quot;: {     &lt;--- Dynamic attribute object
    &quot;type&quot;: &quot;TEXT&quot;,  &lt;--- Field used to map `attribute` dynamic (inside child object)
    &quot;languages&quot;:[
      {
          &quot;language&quot;:&quot;en_EN&quot;,
          &quot;text&quot;:&quot;do you approve?&quot;
      }
    ]
  }
}

Or

{  
  &quot;label&quot;:&quot;Some label&quot;,
  &quot;attribute&quot;: {
    &quot;type&quot;: &quot;NUMBER&quot;,
    &quot;value: &quot;10.0&quot;
  }
}

I am able to deserialize above json properly using @JsonSubTypes with this code:

@Data
public class Field {
    private String label;
    private Attribute attribute;
}

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = &quot;type&quot;)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Attribute.NumberAttribute.class, name = &quot;NUMBER&quot;),
    @JsonSubTypes.Type(value = Attribute.TextAttribute.class, name = &quot;TEXT&quot;)
})
public class Attribute {
    private AttributeType type;

    @Data
    public static class TextAttribute extends Attribute {
        List&lt;Language&gt; languages;
    }

    @Data
    public static class NumberAttribute extends Attribute {
        String value;
    }

    @Data
    public static class Language {
        private String text;
        private String language;
    }
}

However, the problem I have is that I have to use type field coming inside the attribute object, and I would need to move the type to the parent object. The ending json should be like this:

{  
  &quot;type&quot;: &quot;TEXT&quot;,  &lt;--- Field used to map `attribute` dynamic (in parent object)
  &quot;label&quot;:&quot;Some label&quot;,
  &quot;attribute&quot;: {   &lt;--- Dynamic attribute object
    &quot;languages&quot;:[
      {
          &quot;language&quot;:&quot;en_EN&quot;,
          &quot;text&quot;:&quot;do you approve?&quot;
      }
    ]
  }
}

Or

{  
  &quot;type&quot;: &quot;NUMBER&quot;,
  &quot;label&quot;:&quot;Some label&quot;,
  &quot;attribute&quot;: {
    &quot;value: &quot;10.0&quot;
  }
}

I couldn't find any way to use a parent field (or json path way) to use the type property being outside the dynamic subtype. Do you know how I can do this?

答案1

得分: 5

你可以通过在@JsonTypeInfo中添加 include = As.EXTERNAL_PROPERTY 来实现这一点。你只需要将该注解移到字段上。

关于 EXTERNAL_PROPERTY,你可以参考 JavaDoc 中的说明:

类似于 PROPERTY 的包含机制,不同之处在于属性在层次结构中包含在更高的一级 [...]

以下是一个示例:

@Data
class Field {
    private String label;
    private AttributeType attributeType;
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.EXTERNAL_PROPERTY, property = "attributeType")
    private Attribute attribute;
}

@Data
@JsonSubTypes({
        @JsonSubTypes.Type(value = Attribute.NumberAttribute.class, name = "NUMBER"),
        @JsonSubTypes.Type(value = Attribute.TextAttribute.class, name = "TEXT")
})
abstract class Attribute {
    @Data
    public static class TextAttribute extends Attribute {
        List<Language> languages;
    }

    @Data
    public static class NumberAttribute extends Attribute {
        String value;
    }

    @Data
    public static class Language {
        private String text;
        private String language;
    }
}

enum AttributeType {
    NUMBER, TEXT;
}
英文:

You can achieve that by adding include = As.EXTERNAL_PROPERTY to @JsonTypeInfo. You just have to move the annotation to the field.

See the JavaDoc for EXTERNAL_PROPERTY:
> Inclusion mechanism similar to <code>PROPERTY</code>, except that property is included one-level higher in hierarchy [...]

Here's an example:

@Data
class Field {
    private String label;
    private AttributeType attributeType;
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.EXTERNAL_PROPERTY, property = &quot;attributeType&quot;)
    private Attribute attribute;
}

@Data
@JsonSubTypes({
        @JsonSubTypes.Type(value = Attribute.NumberAttribute.class, name = &quot;NUMBER&quot;),
        @JsonSubTypes.Type(value = Attribute.TextAttribute.class, name = &quot;TEXT&quot;)
})
abstract class Attribute {
    @Data
    public static class TextAttribute extends Attribute {
        List&lt;Language&gt; languages;
    }

    @Data
    public static class NumberAttribute extends Attribute {
        String value;
    }

    @Data
    public static class Language {
        private String text;
        private String language;
    }
}

enum AttributeType {
    NUMBER, TEXT;
}

答案2

得分: 1

@Data
public class Field {
    private String label;
    private AttributeType type;

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
    @JsonSubTypes({
        @JsonSubTypes.Type(value = Attribute.NumberAttribute.class, name = "NUMBER"),
        @JsonSubTypes.Type(value = Attribute.TextAttribute.class, name = "TEXT")
    })
    private Attribute attribute;
}

@Data
public class Attribute {
    @Data
    public static class TextAttribute extends Attribute {
        List<Language> languages;
    }

    @Data
    public static class NumberAttribute extends Attribute {
        String value;
    }

    @Data
    public static class Language {
        private String text;
        private String language;
    }
}
英文:

I'm posting this as an alternative in case accepted answer doesn't work for others:

@Data
public class Field {
    private String label;
    private AttributeType type;

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = &quot;type&quot;, include = As.EXTERNAL_PROPERTY)
    @JsonSubTypes({
        @JsonSubTypes.Type(value = Attribute.NumberAttribute.class, name = &quot;NUMBER&quot;),
        @JsonSubTypes.Type(value = Attribute.TextAttribute.class, name = &quot;TEXT&quot;)
    })
    private Attribute attribute;
}

@Data
public class Attribute {
    @Data
    public static class TextAttribute extends Attribute {
        List&lt;Language&gt; languages;
    }

    @Data
    public static class NumberAttribute extends Attribute {
        String value;
    }

    @Data
    public static class Language {
        private String text;
        private String language;
    }
}

huangapple
  • 本文由 发表于 2020年8月18日 01:33:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/63455860.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定