#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());