JAXB удаляет ненужный вложенный тег XML
В настоящее время у меня есть следующий вывод из моей программы:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<container>
<elements>
<property name="e1">
<foo name="Alex" status="Married"/>
</property>
<property name="e2">
<foo name="Chris" status="Married with 2 children"/>
</property>
</elements>
</container>
Как вы можете видеть, имея оба <container>
а также <elements>
Теги бесполезны. Я хотел бы удалить <elements>
так что результат будет выглядеть так:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<container>
<property name="e1">
<foo name="Alex" status="Married"/>
</property>
<property name="e2">
<foo name="Chris" status="Married with 2 children"/>
</property>
</container>
Код, который генерирует первый вывод, приведен ниже:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Container {
@XmlElement
@XmlJavaTypeAdapter(MyAdapter.class)
private Map<String, Foo> elements = new HashMap<String, Foo>();
public Container() {
this.elements = new HashMap<String, Foo>();
}
public Map<String, Foo> getElements() {
return elements;
}
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
@XmlRootElement(name = "foo")
static class Foo {
@XmlAttribute
public String name;
@XmlAttribute
public String status;
public Foo(String name, String status) {
this.name = name;
this.status = status;
}
public Foo() {
}
}
public static void main(String[] args) throws JAXBException {
final JAXBContext context = JAXBContext.newInstance(Container.class);
final Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
final Container c = new Container();
final Map<String, Foo> elementsMap = c.getElements();
elementsMap.put("e1", new Foo("Alex", "Married"));
elementsMap.put("e2", new Foo("Chris", "Married with 2 children"));
m.marshal(c, System.out);
}
}
А также MyAdapter
класс, основанный на JAXB @XmlAdapter: Карта -> Адаптер списка? (только маршалл):
public class MyAdapter extends XmlAdapter<MyAdapter.AdaptedFoo, Map<String, Foo>> {
static class AdaptedFoo {
public List<Property> property = new ArrayList<>();
}
public static class Property {
@XmlAttribute
public String name;
@XmlElementRef(type = Foo.class)
public Foo value;
}
@Override
public Map<String, Foo> unmarshal(AdaptedFoo v) throws Exception {
return null;
}
@Override
public AdaptedFoo marshal(Map<String, Foo> map) throws Exception {
if (null == map) {
return null;
}
AdaptedFoo adaptedFoo = new AdaptedFoo();
for (Entry<String, Foo> entry : map.entrySet()) {
Property property = new Property();
property.name = entry.getKey();
property.value = entry.getValue();
adaptedFoo.property.add(property);
}
return adaptedFoo;
}
}
Как я могу удалить <elements>
тег из моего вывода?
редактировать: я нашел "грязный" способ сделать это - настройка @XmlRootElement(name = "")
за Container
класс Но есть ли "элегантный" способ?
2 ответа
Элемент XML <elements>
требуется связать последовательность вложенного элемента со свойством Map<?,?> elements
, Вы не можете бросить это: как бы маршаллер узнал, где <property>
элементы принадлежат * на уровне элемента <container>
,
Иметь List<Property>
отличается, так как JAXB обрабатывает повторяющиеся элементы х "аппаратно" как List<?> x
, так что нет необходимости в обертке.
Поскольку вы пишете Java-классы с аннотациями, вы можете использовать это, добавив (к контейнеру) другое "виртуальное" поле и изменив некоторые аннотации:
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Container {
// ...
@XmlTransient
public Map<String,Foo> getElements(){
return elements;
}
private List<Property> property;
@XMLElement
public List<Property> getProperty(){
List<Property> props = elements.entrySet().stream()
.map( e -> new Property( e.getKey(), e.getValue() )
.collect( Collectors.toList() );
return props;
}
Старые Javas могли бы сделать
List<Property> props = new ArrayList<>();
for( Map.Entry<String,Foo> e: elements.entrySet() ){
props.add( new Property( e.getKey(), e.getValue() ) );
}
return props;
Это маршалы вроде этого:
<container>
<property name="aaa">
<foo name="aaaname" status="aaastatus"/>
</property>
<property name="bbb">
<foo name="bbbname" status="bbbstatus"/>
</property>
</container>
Это не демаршал!
Кроме удаления тега путем "взлома" имени элемента на "", я не думаю, что вы сможете обойтись без использования этого типа данных без вложенного тега.
Тег описывает тип данных, который охватывает ваш адаптированный класс, который вы аннотировали как элемент XML с именем elements
, который содержит свойства, определенные вашим адаптером.
Если вы хотите что-то, что более близко соответствует тому, что, я думаю, вам нужно, либо измените контекст вашего корневого класса, либо, возможно, используйте List
вместо HashMap
в вашем классе контейнера.