如何serialize/deserialize"splashed"复杂类型?

How to serialize/deserialize "splashed" complex type?

我正在连接到外部 XML API,我正在尝试使用 Jackson XmlMapper class 将其解析为 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 元素。

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

I want to parse the currency and cost elements in a single Money object.

鉴于提供的 XML,您可以通过为发票创建值对象并利用@JsonUnwrapped.

为发票创建值对象

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

创建 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。这可以在字段上、构造函数内部或 setter 上,它将 "unwrap" Money 对象并在与 Invoice 相同的级别反序列化其成员.像这样构造您的 class 以将货币和成本反序列化为单个对象:

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;
    }
}

the inner items only specify the cost and "reuse" the currency code.

尽可能分离 Money 和 "item" 模型

为两个不同的模型重用 Money 对象不如用一个 abject 来表示每个视图那么理想。例如,currencycostMoney 对象以及 iddescriptioncostItem 对象.如果这在您的项目中可行,我会为 "item":

创建一个这样的对象
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;
    }
}

为 "item" 值重复使用 Money

如果您没有创建新对象的自由并且需要重用 Money,您可以配置 XmlMapper 以忽略未知属性并将所有属性放在 Money对象。

配置 XmlMapper 以忽略未知属性

我建议像这样扩展 XmlMapper

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

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

这将在元素存在时填充属性。例如,当 "item" 被反序列化时: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;
    }
}