Простой xml возвращает нулевое значение для поля атрибута

#java #xml #simple-framework

#java #xml #simple-framework

Вопрос:

Я хочу использовать простой XML для десериализации следующего XML в POJO:

 <shippingInfo>
   <shippingServiceCost currencyId="USD">9.8</shippingServiceCost>
   <shippingType>Flat</shippingType>
   <shipToLocations>Worldwide</shipToLocations>
   <expeditedShipping>true</expeditedShipping>
   <oneDayShippingAvailable>false</oneDayShippingAvailable>
   <handlingTime>3</handlingTime>
</shippingInfo>
  

Для этого я создал следующий класс. Однако у меня возникли проблемы из-за того, что атрибут CurrencyID не десериализуется должным образом.

 @Root(name = "shippingInfo")
public class ShippingInfo {

    @Element(name = "shippingServiceCost", required = false)
    private BigDecimal shippingServiceCost;

    @Attribute(name = "currencyId", required = false)
    private String currencyId;

    @Element(name = "shippingType", required = false)
    private String shippingType;

    @Element(name = "shipToLocations" ,required = false)
    private String shipToLocations;

    @Element(name = "expeditedShipping", required = false)
    private Boolean expeditedShipping;

    @Element(name = "oneDayShippingAvailable", required = false)
    private Boolean oneDayShippingAvailable;

    @Element(name = "handlingTime", required = false)
    private Integer handlingTime;

    // Getters amp; Setters

    public BigDecimal getShippingServiceCost() {
        return shippingServiceCost;
    }

    public void setShippingServiceCost(BigDecimal shippingServiceCost) {
        this.shippingServiceCost = shippingServiceCost;
    }

    public String getCurrencyId() {
        return currencyId;
    }

    public void setCurrencyId(String currencyId) {
        this.currencyId = currencyId;
    }

    public String getShippingType() {
        return shippingType;
    }

    public void setShippingType(String shippingType) {
        this.shippingType = shippingType;
    }

    public String getShipToLocations() {
        return shipToLocations;
    }

    public void setShipToLocations(String shipToLocations) {
        this.shipToLocations = shipToLocations;
    }

    public Boolean isExpeditedShipping() {
        return expeditedShipping;
    }

    public void setExpeditedShipping(Boolean bool) {
        this.expeditedShipping = bool;
    }

    public Boolean isOneDayShippingAvailable() {
        return oneDayShippingAvailable;
    }

    public void setOneDayShippingAvailable(Boolean bool) {
        this.oneDayShippingAvailable = bool;
    }

    public Integer getHandlingTime() {
        return handlingTime;
    }

    public void setHandlingTime(Integer days) {
        this.handlingTime = days;
    }
}
  

Я бы ожидал, что значение CurrencyID будет «USD» после десериализации, но я получаю null. Кажется, что все значения элементов десериализуются должным образом. У кого-нибудь есть предложения о том, как это исправить?

Более того, в таком случае, как следующий экземпляр:

 <sellingStatus>
    <currentPrice currencyId="USD">125.0</currentPrice>
    <convertedCurrentPrice currencyId="USD">125.0</convertedCurrentPrice>
    <bidCount>2</bidCount>
    <sellingState>EndedWithSales</sellingState>
</sellingStatus>
  

Где есть два атрибута с именем CurrencyID для двух разных элементов, как я могу десериализовать их в отдельные поля? Я создал аналогичный класс SellingStatus, но не уверен, как различать атрибуты CurrencyID.

Спасибо!

Редактировать: Согласно предложениям, я попытался добавить пользовательский класс ShippingServiceCost в ShippingInfo следующим образом:

 @Element(name = "shippingServiceCost", required = false)
    private ShippingServiceCost shippingServiceCost;
  

Которое, в свою очередь, выглядит следующим образом:

 public class ShippingServiceCost {

    @Element(name = "shippingServiceCost", required = false)
    private BigDecimal shippingServiceCost;

    @Attribute(name = "currencyId", required = false)
    private String currencyId;

    // getters and setters
}
  

Но когда я пытаюсь получить доступ как к полю shippingServiceCost, так и к полю CurrencyID, я получаю null в каждом экземпляре (хотя я знаю, что данные есть). Любые предложения приветствуются.

Комментарии:

1. Удалите атрибут CurrencyID из класса shipping info. Создайте отдельный класс для стоимости услуги доставки и добавьте туда атрибут CurrencyID. Затем свяжите класс стоимости службы доставки с классом информации о доставке.

2. Смотрите мою правку выше, я пробовал это, но я все еще получаю null как для shippingServiceCost, так и для CurrencyID.

3. Можете ли вы прокомментировать свой класс shippingServiceCost следующим «@XmlAccessorType(XmlAccessType. ПОЛЕ)»

4. Я пробовал это, но все еще получаю нулевые значения.

Ответ №1:

Для приведенного выше кода SimpleXML ожидает, что CurrencyID будет присутствовать как <shippingInfo currencyId="USD"> .

Итак, чтобы решить эту проблему, вам нужно создать другой класс с именем ShippingServiceCost, который будет содержать атрибут CurrencyID и BigDecimal Это также решит ваш второй запрос. Вы можете сделать это, создав два класса CurrentPrice и ConvertedCurrentPrice , которые будут содержать атрибут CurrencyID.

Комментарии:

1. Это на самом деле то, что я пробовал в первую очередь. Однако, когда я делал это, я получал нулевые значения как для стоимости доставки, так и для идентификатора валюты. Размещение стоимости услуги доставки в классе Shipping Info решило эту проблему, и теперь я могу получить к нему доступ, но я не могу получить идентификатор валюты.

2. Обновление: я пробовал это (см. Редактирование исходного сообщения), но я все еще получаю нулевые значения.

Ответ №2:

Единственным рабочим решением является создание класса Converter, смотрите код ниже:

 public class ShippingInfoConverter implements Converter<ShippingInfo> {

@Override
public ShippingInfo read(InputNode inputNode) throws Exception {
    ShippingInfo shippingInfo = new ShippingInfo();
    InputNode shippingServiceCostNode = inputNode.getNext("shippingServiceCost");
    shippingInfo.setShippingServiceCost(new BigDecimal(shippingServiceCostNode.getValue()));
    shippingInfo.setCurrencyId(shippingServiceCostNode.getAttribute("currencyId").getValue());
    shippingInfo.setShippingType(inputNode.getNext("shippingType").getValue());
    shippingInfo.setShipToLocations(inputNode.getNext("shipToLocations").getValue());
    shippingInfo.setExpeditedShipping(Boolean.parseBoolean(inputNode.getNext("expeditedShipping").getValue()));
    shippingInfo.setOneDayShippingAvailable(Boolean.parseBoolean(inputNode.getNext("oneDayShippingAvailable").getValue()));
    shippingInfo.setHandlingTime(Integer.valueOf(inputNode.getNext("handlingTime").getValue()));
    return shippingInfo;
}

@Override
public void write(OutputNode outputNode, ShippingInfo shippingInfo) throws Exception {
    OutputNode shippingServiceCostNode = outputNode.getChild("shippingServiceCost");
    shippingServiceCostNode.setValue(shippingInfo.getShippingServiceCost().toString());
    shippingServiceCostNode.setAttribute("currencyId", shippingInfo.getCurrencyId());
    outputNode.getChild("shippingType").setValue(shippingInfo.getShippingType());
    outputNode.getChild("shipToLocations").setValue(shippingInfo.getShipToLocations());
    outputNode.getChild("expeditedShipping").setValue(Boolean.toString(shippingInfo.isExpeditedShipping()));
    outputNode.getChild("oneDayShippingAvailable").setValue(Boolean.toString(shippingInfo.isOneDayShippingAvailable()));
    outputNode.getChild("handlingTime").setValue(Integer.toString(shippingInfo.getHandlingTime()));
}
  

}

Обратите внимание, как устанавливается ‘CurrencyID’, используя метод getAttribute узла.

 shippingInfo.setCurrencyId(shippingServiceCostNode.getAttribute("currencyId").getValue());
  

Также обратите внимание, как элемент ‘shippingServiceCost’ получает атрибут

 shippingServiceCostNode.setAttribute("currencyId", shippingInfo.getCurrencyId());
  

Для того, чтобы это заработало, необходимо выполнить несколько других действий, начиная с вашего POJO

 @Root(name = "shippingInfo")
@Convert(ShippingInfoConverter.class)
public class ShippingInfo {

    @Element(name = "shippingServiceCost", required = false)
    private BigDecimal shippingServiceCost;

    private String currencyId;

    @Element(name = "shippingType", required = false)
    private String shippingType;

    @Element(name = "shipToLocations" ,required = false)
    private String shipToLocations;

    @Element(name = "expeditedShipping", required = false)
    private Boolean expeditedShipping;

    @Element(name = "oneDayShippingAvailable", required = false)
    private Boolean oneDayShippingAvailable;

    @Element(name = "handlingTime", required = false)
    private Integer handlingTime;

    // Getters amp; Setters

    public BigDecimal getShippingServiceCost() {
        return shippingServiceCost;
    }

    public void setShippingServiceCost(BigDecimal shippingServiceCost) {
        this.shippingServiceCost = shippingServiceCost;
    }

    public String getCurrencyId() {
        return currencyId;
    }

    public void setCurrencyId(String currencyId) {
        this.currencyId = currencyId;
    }

    public String getShippingType() {
        return shippingType;
    }

    public void setShippingType(String shippingType) {
        this.shippingType = shippingType;
    }

    public String getShipToLocations() {
        return shipToLocations;
    }

    public void setShipToLocations(String shipToLocations) {
        this.shipToLocations = shipToLocations;
    }

    public Boolean isExpeditedShipping() {
        return expeditedShipping;
    }

    public void setExpeditedShipping(Boolean bool) {
        this.expeditedShipping = bool;
    }

    public Boolean isOneDayShippingAvailable() {
        return oneDayShippingAvailable;
    }

    public void setOneDayShippingAvailable(Boolean bool) {
        this.oneDayShippingAvailable = bool;
    }

    public Integer getHandlingTime() {
        return handlingTime;
    }

    public void setHandlingTime(Integer days) {
        this.handlingTime = days;
    }

}
  

Добавление строки ниже указывает SimpleXML на класс converter

 @Convert(ShippingInfoConverter.class)
  

Другим изменением является удаление аннотации @Attribute.
И последнее, что требуется, это то, что в вашем классе драйвера должна быть включена AnnotationStrategy
при сериализации и десериализации ваших объектов.

 Serializer serializer = new Persister(new AnnotationStrategy());