Статьи

Неверное отображение XML — перечисленные элементы коллекции


В 
предыдущем посте  я представил 
EclipseLink JAXB (Moxy) «сек 
@XmlVariableNode  расширение. В этом посте я продемонстрирую, как 
 можно использовать
@XmlVariableNode для решения 
интересного вопроса, с которым я столкнулся при переполнении стека . В этом вопросе вместо коллекции, представленной с элементом, который появлялся несколько раз, имя элемента содержало индекс. Хотя я бы никогда не рекомендовал структурировать ваш XML-документ таким образом, иногда вы сталкиваетесь с ним и должны иметь возможность отобразить его.

XML Ввод (input.xml) / Вывод 


Ниже показано, как выглядит проблемный XML.
В этом примере количество различных элементов с префиксом 
номера телефона  неизвестно.
<?xml version="1.0" encoding="UTF-8"?>
<customer>
    <phone-number1 type="work">555-1111</phone-number1>
    <phone-number2 type="home">555-2222</phone-number2>
    <phone-number3 type="cell">555-3333</phone-number3>
</customer>

Модель Java

Ниже приведена модель Java, которую мы будем использовать в этом примере.

Клиент

В конечном итоге мы хотим представить XML в Java как  Customer  , у которого есть коллекция   экземпляров PhoneNumber . Для этого варианта использования мы будем использовать  расширение @XmlVariableNode от  MOXy . Указанный объект не имеет свойства, которое мы можем использовать для хранения имени узла, поэтому мы будем использовать  XmlAdapter  для преобразования  PhoneNumber  в объект, который имеет.

package blog.variablenode.enumeratedlist;
 
import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlVariableNode;
 
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
 
    @XmlVariableNode("nodeName")
    @XmlJavaTypeAdapter(PhoneNumberAdapter.class)
    private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
 
}
Номер телефона

Обратите внимание, что  PhoneNumber  не имеет поля для хранения имени узла

package blog.variablenode.enumeratedlist;
 
import javax.xml.bind.annotation.*;
 
@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {
 
    @XmlAttribute
    private String type;
 
    @XmlValue
    private String number;
 
}
XMLAdapter (PhoneNumberAdapter)

Мы будем использовать  XmlAdapter  для преобразования  PhoneNumber  в  AdaptedPhoneNumberAdaptedPhoneNumber  имеет  поле nodeName (строка 14), на которое мы ссылались в   аннотации @XmlVariableNode, которую мы использовали в  поле phoneNumbers в   классе Customer . Мы аннотируем это поле с помощью  @XmlTransient,  чтобы предотвратить его маршалинг / демаршаллинг (см.  JAXB и Unmapped Properties ). Мы будем использовать расширение @XmlPath  MOXy,  чтобы избежать необходимости копировать содержимое  PhoneNumber  в  AdaptedPhoneNumber (см .:  XPath Based Mapping ).

package blog.variablenode.enumeratedlist;
 
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
 
public class PhoneNumberAdapter extends XmlAdapter<PhoneNumberAdapter.AdaptedPhoneNumber, PhoneNumber>{
 
    private int counter = 1;
 
    public static class AdaptedPhoneNumber {
 
        @XmlTransient
        public String nodeName;
 
        @XmlPath(".")
        public PhoneNumber phoneNumber;
 
    }
 
    @Override
    public AdaptedPhoneNumber marshal(PhoneNumber phoneNumber) throws Exception {
        AdaptedPhoneNumber adaptedPhoneNumber = new AdaptedPhoneNumber();
        adaptedPhoneNumber.nodeName = "phone-number" + counter++;
        adaptedPhoneNumber.phoneNumber = phoneNumber;
        return adaptedPhoneNumber;
    }
 
    @Override
    public PhoneNumber unmarshal(AdaptedPhoneNumber adaptedPhoneNumber) throws Exception {
        return adaptedPhoneNumber.phoneNumber;
    }
 
}
демонстрация

Следующий демонстрационный код может быть использован, чтобы доказать, что все работает. Поскольку PhoneNumberAdapter  является состоящим из состояния, нам нужно установить его экземпляр  на  маршаллере (строка 17)

package blog.variablenode.enumeratedlist;
 
import java.io.File;
import javax.xml.bind.*;
 
public class Demo {
 
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);
         
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/blog/variablenode/enumeratedlist/input.xml");
        Customer customer = (Customer) unmarshaller.unmarshal(xml);
         
        Marshaller marshaller = jc.createMarshaller();
        PhoneNumberAdapter phoneNumberAdapter = new PhoneNumberAdapter();
        marshaller.setAdapter(phoneNumberAdapter);
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }
 
}