【问题标题】:How to serialize/deserialize "splashed" complex type?如何序列化/反序列化“飞溅”的复杂类型?
【发布时间】:2015-07-21 23:07:15
【问题描述】:

我正在连接到外部 XML API,并尝试使用 Jackson XmlMapper 类将其解析为 POJO。部分 XML 如下所示:

<invoice>
    <some>element</some>
    <some_other>element</some_other>
    <currency>USD</currency>
    <cost>10.42</cost>
    <breakdown>
        <item id="1">
            <description>blah blah</description>
            <cost>4.21</cost>
        </item>
    </breakdown>
</invoice>

我想在单个 Money 对象中解析 currencycost 元素。

更糟糕的是,内部items 仅指定成本并“重用”货币代码。我可以使用 Jackson 以某种智能方式解析它们吗?

【问题讨论】:

    标签: java xml jackson


    【解决方案1】:

    我想在单个 Money 对象中解析货币和成本元素。

    鉴于提供的 XML,您可以通过为发票创建一个值对象并利用 @JsonUnwrappedcurrencycost 元素解析为单个 Money 对象。

    为发票创建一个值对象

    如果您不想为发票创建对象,则可以改为将 XmlMapper 配置为忽略未知属性,并将整个响应反序列化为 Money 对象。在我看来,为您的发票创建一个单独的类是一种更简洁的方法。

    创建Invoice 对象的目的是封装响应的所有元素。现在您可能只需要currencycost,但稍后您可能需要访问breakdown。对象可以这样构造:

    public class Invoice {
        private final String some;
        private final String some_other;
        @JsonUnwrapped
        private final Money money;
        private final List<Item> breakdown;
    
        @JsonCreator
        public Invoice(@JsonProperty("some") String some,
                       @JsonProperty("some_other") String some_other,
                       @JsonProperty("money") Money money,
                       @JacksonXmlProperty(localName = "item") List<Item> breakdown) {
            this.some = some;
            this.some_other = some_other;
            this.money = money;
            this.breakdown = breakdown;
        }
    
        public String getSome() {
            return some;
        }
    
        public String getSome_other() {
            return some_other;
        }
    
        public Money getMoney() {
            return money;
        }
    
        public List<Item> getBreakdown() {
            return breakdown;
        }
    }
    

    请注意,Money 属性使用@JsonUnwrapped 进行注释。这可以在字段上、构造函数内部或设置器上,它将“解包”Money 对象并在与Invoice 相同的级别上反序列化其成员。像这样构造您的类,以将货币和成本反序列化为单个对象:

    public class Money {
        private final String currency;
        private final Double cost;
    
        @JsonCreator
        public Money(@JsonProperty("currency") String currency,
                     @JsonProperty("cost") Double cost) {
            this.currency = currency;
            this.cost = cost;
        }
    
        public String getCurrency() {
            return currency;
        }
    
        public Double getCost() {
            return cost;
        }
    }
    

    内部项目仅指定成本并“重复使用”货币代码。

    如果可能,将 Money 和“item”模型分开

    为两个不同的模型重用Money 对象不如让一个abject 代表每个视图那么理想。例如,currencycostMoney 对象,iddescriptioncostItem 对象。如果这在您的项目中可行,我将为“项目”创建一个像这样的对象:

    public class Item {
        @JacksonXmlProperty(isAttribute = true)
        public final String id;
        public final String description;
        public final Double cost;
    
        @JsonCreator
        public Item(@JsonProperty("id") String id,
                    @JsonProperty("description") String description,
                    @JsonProperty("cost") Double cost) {
            this.id = id;
            this.description = description;
            this.cost = cost;
        }
    
        public String getId() {
            return id;
        }
    
        public String getDescription() {
            return description;
        }
    
        public Double getCost() {
            return cost;
        }
    }
    

    Money 用于“项目”值

    如果您无权创建新对象并需要重用Money,您可以将XmlMapper 配置为忽略未知属性并将所有属性放在Money 对象上。

    XmlMapper 配置为忽略未知属性

    我建议像这样扩展XmlMapper

    public class CustomXmlMapper extends XmlMapper {
        public CustomXmlMapper() {
            configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        }
    }
    

    将所有可能的属性添加到Money

    这将在元素存在时填充属性。例如,当反序列化“项目”时:iddescriptioncost 将被填充,currency 将为空。

    public class Money {
        @JacksonXmlProperty(isAttribute = true)
        public final String id;
        public final String description;
        private final String currency;
        private final Double cost;
    
        @JsonCreator
        public Money(@JsonProperty("id") String id,
                     @JsonProperty("description") String description,
                     @JsonProperty("currency") String currency,
                     @JsonProperty("cost") Double cost) {
            this.id = id;
            this.description = description;
            this.currency = currency;
            this.cost = cost;
        }
    
        public String getId() {
            return id;
        }
    
        public String getDescription() {
            return description;
        }
    
        public String getCurrency() {
            return currency;
        }
    
        public Double getCost() {
            return cost;
        }
    }
    

    【讨论】:

    • 绝妙的答案!谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多