Reputation: 1704
Currently I have following output from my program:
<?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>
As you can see, having both <container>
and <elements>
tags is useless. I'd like to remove <elements>
, so the output would look like:
<?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>
Code that's generating first output is listed below:
@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);
}
}
And MyAdapter
class, based on JAXB @XmlAdapter: Map -> List adapter? (marshall only) :
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;
}
}
How can I remove <elements>
tag from my output?
edit: I've found 'dirty' way to do it - setting @XmlRootElement(name = "")
for Container
class. But is there any 'elegant' way?
Upvotes: 0
Views: 4866
Reputation: 31300
The XML element <elements>
is required to associate the enclosed element seqence with the property Map<?,?> elements
. You can't drop it: how would an unmarshaller know where the <property>
elements belong *on the level of the element <container>
.
Having a List<Property>
is different since JAXB handles repeated elements x "hardwired" as a List<?> x
, so there's no need for a wrapper.
Since you write Java classes with annotations, you could use this by adding (to Container) another "virtual" field and modify some annotations:
@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;
}
Older Javas might do
List<Property> props = new ArrayList<>();
for( Map.Entry<String,Foo> e: elements.entrySet() ){
props.add( new Property( e.getKey(), e.getValue() ) );
}
return props;
This marshals like this:
<container>
<property name="aaa">
<foo name="aaaname" status="aaastatus"/>
</property>
<property name="bbb">
<foo name="bbbname" status="bbbstatus"/>
</property>
</container>
It doesn't unmarshal!
Upvotes: 1
Reputation: 8323
Other than removing the tag by "hacking" the element name to be "", I don't think you're going to be able to get around using that data type without the nested tag.
The tag is describing the data type that encompasses your adapted class, which you've annotated as an XML element called elements
, which contains properties defined by your adapter.
If you want something that more closely matches what I think you are after, either change the context of your root class, or maybe use a List
instead of a HashMap
in your container class.
Upvotes: 0