Jaxb unmarshal xml sequence to map - java

I have two classes Foos and Foo that are supposed to be serializable/deserializable to/from XML:
#XmlRootElement(name = "foos")
#XmlAccessorType(XmlAccessType.NONE)
#XmlType(name = "Foos", propOrder = { "foo" })
public class Foos {
protected Map<Foo, Foo> foo;
public Map<Foo, Foo> getFooMap() {
if (foo == null) {
foo = new HashMap<Foo, Foo>();
}
return this.foo;
}
#XmlElements(value = { #XmlElement })
public Collection<Foo> getFoo() {
return getFooMap().values();
}
}
and
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Foo", propOrder = { "name" })
public class Foo {
protected String name;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
}
For algorithmical reasons I cannot use a List<Foo> but need a Map<Foo, Foo>. Each Foo should be put into the Map both as key and value.
Marshalling a Foos object to XML works because I have the getFoo getter, but unmarshalling from XML does not. This is probably because the unmarshaller does not know how to convert the xml sequence of Foo objects into a hash map.
How can I tell this to the unmarshaller?
I think I need a method like this (pseudocode):
public void fillMap(XMLSequence<Foo> sequence) {
for (Foo foo : sequence)
getFooMap().put(foo, foo);
}
And then it probably needs some xml annotations as well.
Any hints are appreciated!

I found a way using two fields:
#XmlRootElement(name = "foos")
#XmlAccessorType(XmlAccessType.NONE)
#XmlType(name = "Foos", propOrder = { "fooList", "foo" })
public class Foos {
#XmlElement(name="foo")
protected List<Foo> fooList;
protected Map<Foo, Foo> foo;
public Map<Foo, Foo> getFooMap() {
if (foo == null) {
foo = new HashMap<Foo, Foo>();
if (fooList != null)
for (Foo f : fooList)
foo.put(f, f);
}
return this.foo;
}
#XmlElement
public Collection<Foo> getFoo() {
return getFooMap().values();
}
}
During unmarshalling, the fooList is filled with the data from the <foo>...</foo> xml elements.
When getFooMap is called for the first time, it is filled with the content of fooList (by putting the same object as key and value).
During marshalling, the method getFoo is called which will return the values of the foo map.

Related

#XmlRootElement on class but not in xml

I have a class that I'm using to generate an xml payload from:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "someName", propOrder = {
"one",
"two"
})
#XmlRootElement(name = "test")
public class MyClass {
#XmlElement
protected String one;
#XmlElement
protected String two;
...
}
With an object factory method as follows
#XmlElementDecl("Something")
public JAXBElement<MyClass> getMyClassXml(MyClass value) {
return new JAXBElement<MyClass>(_Something_QNAME, MyClass.class, null, value);
}
I would like the soap body to contain
<Something>
<test>
<one>1</one>
<two>2</two>
</test>
</Something>
but I end up with
<Something>
<one>1</one>
<two>2</two>
</Something>
Has anyone come across something similar?
If you put your class into a list and then save that list in XML it will come out similar to what you're looking for.
You can try the following code.
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
public class MyClass {
#XmlElement
protected String one;
#XmlElement
protected String two;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public String getTwo() {
return two;
}
public void setTwo(String two) {
this.two = two;
}
}
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "Something")
#XmlAccessorType(XmlAccessType.FIELD)
public class Something {
#XmlElement(name = "test")
private MyClass testClass;
public MyClass getTestClass() {
return testClass;
}
public void setTestClass(MyClass testClass) {
this.testClass = testClass;
}
}
Test class to verify.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Test {
public static void main(String[] args) throws Exception {
MyClass testClass = new MyClass();
testClass.setOne("1");
testClass.setTwo("2");
Something something = new Something();
something.setTestClass(testClass);
JAXBContext jaxbContext = JAXBContext.newInstance(Something.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(something, System.out);
}
}
After running the test class, you will find the following xml structure.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Something>
<test>
<one>1</one>
<two>2</two>
</test>
</Something>
So, in your case, Something should contain MyClass as nested object to generate the desired XML structure using Jaxb.
try with this,
Something.java
#XmlRootElement
public class Something {
#XmlElement(name = "test")
private List<Test> testList;
public List<Test> getTestList() {
return testList;
}
public void setTestList(List<Test> testList) {
this.testList = testList;
}
}
Test.java
#XmlRootElement(name = "test")
public class Test {
#XmlElement
private String one;
#XmlElement
private String two;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public String getTwo() {
return two;
}
public void setTwo(String two) {
this.two = two;
}
}
Marshalling with JAXB
//Create a test
Test test = new Test();
test.setOne("1");
test.setTwo("2");
//Create a something
Something something = new Something();
//Add test into the testList of something
something.getTestList().add(test);
JAXBContext jaxbContext = JAXBContext.newInstance(Something.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(something, new File("something.xml"));
something.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Something>
<test>
<one>1</one>
<two>2</two>
</test>
</Something>
I solved it by modifying the xml class that wsimport generated as follows:
// #XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "someName"/*, propOrder = {
"one",
"two"
} */ )
// #XmlRootElement(name = "test")
#XmlAccessorType(XmlAccessType.NONE)
public class MyClass {
// #XmlElement
protected String one;
// #XmlElement
protected String two;
...
// my own custom wrapper
#XmlElement
protected MyClassWrapper test = new MyClassWrapper(this);
}
Where my wrapper looks something like this:
#XmlAccessorType(XmlAccessType.NONE)
#XmlType(name = "someNameWrapper", propOrder = {
"one",
"two"
})
public class MyClassWrapper {
public MyClassWrapper() {}
public MyClassWrapper(MyClass base) {
this.base = base;
}
#XmlElement
public getOne() { return base.getOne(); }
#XmlElement
public getTwo() { return base.getTwo(); }
}

Deserializing XML with list using Jackson

I have the following XML that I'd like to deserialize to Java POJO.
<testdata>
<foo>
<bar>
<![CDATA[MESSAGE1]]>
</bar>
<bar>
<![CDATA[MESSAGE2]]>
</bar>
<bar>
<![CDATA[MESSAGE3]]>
</bar>
</foo>
</testdata>
I have the following Java classes
public class TestData {
#JacksonXmlProperty(localName = "foo")
private Foo foo;
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
}
I have another class like below
public class Foo {
#JacksonXmlProperty(localName = "bar")
#JacksonXmlCData
private List<String> barList;
public List<String> getBarList() {
return barList;
}
public void setBarList(List<String> barList) {
this.barList = barList;
}
}
Now when I run the code using the class below I get an exception
private void readXml() throws FileNotFoundException, IOException {
File file = new File("/Users/temp.xml");
XmlMapper xmlMapper = new XmlMapper();
String xml = GeneralUtils.inputStreamToString(new FileInputStream(file));
TestData testData = xmlMapper.readValue(xml, TestData.class);
System.out.println(testData.getFoo()
.getBarList());
}
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.util.ArrayList out of VALUE_STRING token
How do I convert bar elements into a List? I tried multiple things but I keep getting some or the other errors
You need to indicate that <bar> is a wrapping element for your collection of String messages:
This should work in your Foo class:
#JacksonXmlProperty(localName = "bar")
#JacksonXmlCData
#JacksonXmlElementWrapper(useWrapping = false)
private List<String> barList;
In case you have in your input xml a list of bar elements with an attribute like
<testdata>
<foo>
<bar name="John">
<![CDATA[MESSAGE1]]>
</bar>
<bar name="Mary">
<![CDATA[MESSAGE2]]>
</bar>
<bar name="Bill">
<![CDATA[MESSAGE3]]>
</bar>
</foo>
<testdata>
you could create a Bar class and include a list of it as a field of the Foo class:
#JacksonXmlProperty(localName = "bar")
#JacksonXmlElementWrapper(useWrapping = false)
private List<Bar> barList;
The Bar class would be:
class Bar {
#JacksonXmlProperty(isAttribute = true)
private String name;
#JacksonXmlCData
private String content;
}
Remember to include getters and setters for the Bar class.

jaxb delete list tag

I need to marshall a java class to get a xml, but i don't know how to delete a tag inside the one generated.
I have a class with an object list with this form
#XmlRootElement(name = "Element")
public class Element {
private List<Foo> foos;
#XmlElementWrapper("fooList")
public List<Foo> getfoos() {
return foos;
}
public void setFoos(List<Foo> foos) {
this.foos = foos;
}
}
And the class Foo of the list is lie this:
#XmlRootElement
public class Foo {
private String id;
private String code;
#XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlElement
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
When marshalling to get xml I get this:
<Element>
<fooList>
<foos>
<string1>asd</string1>
<string2>qwe</string2>
</foos>
<foos>
<string1>poi</string1>
<string2>lkj</string2>
</foos>
</fooList>
</Element>
But I want to get it without the tag foos, like this:
<Element>
<fooList>
<string1>asd</string1>
<string2>qwe</string2>
<string1>poi</string1>
<string2>lkj</string2>
</fooList>
</Element>
Can anyone help me?
Thanks a lot!!
You could do something like this:
#XmlRootElement(name = "Element")
#XmlAccessorType(XmlAccessType.FIELD)
public class Element {
#XmlElementWrapper(name = "fooList")
#XmlElements({
#XmlElement(name = "id", type = Id.class),
#XmlElement(name = "code", type = Code.class),
})
private List<FooItem> foos;
public List<FooItem> getfoos() {
return foos;
}
public void setFoos(List<FooItem> foos) {
this.foos = foos;
}
}
and then Id and Code classes look similar:
public class Id implements FooItem {
#XmlValue
private String id;
public Id() {}
public Id(String id) {
this.id = id;
}
}
They are bounded by an interface that doesn't do much:
public interface FooItem { }
This structure will allow you to marshal into xml as the one you specified you need.
The challenge with the class structure you had is that class Foo had 2 fields and #XmlValue can be applied only to one field per class. So having 2 fields "forces" them to stand for #XmlElement and they in turn have to be children of an xml element. This is why you had the "intermediate" foo elements in your xml for each Foo instance in your List.

JAXB Element With Specific Type But Unknown Names

I have a root Xml document (name = "Entity") that contains one known Xml element (name = "Header") and another Xml element of unknown name but it is known to have an inner XmlElement(name="label")
Here are possible Xmls:
<Entity>
<Header>this is a header</Header>
<a>
<label>this is element A</label>
<otherElements/>
</a>
</Entity>
<Entity>
<Header>this is a different header</Header>
<b>
<label>this is some other element of name b</label>
<others/>
</b>
</Entity>
Here are my JAXB annotated classes:
#XmlRootElement(name = "Entity")
#XmlAccessorType(XmlAccessType.NONE)
public class Entity {
#XmlElement(name = "Header")
private Header header;
#XmlElements( {
#XmlElement(name = "a", type=LabelledElement.A.class),
#XmlElement(name = "b", type=LabelledElement.B.class)
} )
private LabelledElement labelledElement;
// constructors, getters, setters...
}
#XmlAccessorType(XmlAccessType.NONE)
public abstract class LabelledElement {
#XmlElement
private String label;
#XmlAnyElement
private List<Element> otherElements;
public static class A extends LabelledElement {}
public static class B extends LabelledElement {}
}
This was working great! But then I noticed that it isn't only <a> and <b>
It could be <c>, <asd> and even <anything>...
So listing the XmlElement(name = "xyz", type = LabelledElement.xyz.class) is obviously not the right solution.
All I care about is Entity#getLabelledElement()#getLabel() no matter what the LabelledElement name is.
Is this even possible with JAXB?
With EclipseLink JAXB Implementation (MOXy), this should work :
#XmlRootElement(name = "Entity")
#XmlSeeAlso({LabelledElement.class}) //Might not be necessary
#XmlAccessorType(XmlAccessType.NONE)
public class Entity {
#XmlElement(name = "Header")
private Header header;
#XmlPath("child::*[position() = 2]")
#XmlJavaTypeAdapter(MapAdapter.class)
private Map<String,LabelledElement> labelledElementMap;
public LabelledElement getLabelledElement(){
return labelledElementMap.values().get(0);
}
// constructors, getters, setters...
}
The MapAdapter class :
public class MapAdapter extends XmlAdapter<MapAdapter.AdaptedMap, Map<String, LabelledElement>> {
public static class AdaptedMap {
#XmlVariableNode("key")
List<LabbeledElement> entries = new ArrayList<LabbeledElement>();
}
public static class AdaptedEntry {
#XmlTransient
public String key;
#XmlElement
public LabelledElement value;
}
#Override
public AdaptedMap marshal(Map<String, LabelledElement> map) throws Exception {
AdaptedMap adaptedMap = new AdaptedMap();
for(Entry<String, LabelledElement> entry : map.entrySet()) {
AdaptedEntry adaptedEntry = new AdaptedEntry();
adaptedEntry.key = entry.getKey();
adaptedEntry.value = entry.getValue();
adaptedMap.entries.add(adaptedEntry);
}
return adaptedMap;
}
#Override
public Map<String, LabelledElement> unmarshal(AdaptedMap adaptedMap) throws Exception {
List<AdaptedEntry> adaptedEntries = adaptedMap.entries;
Map<String, LabelledElement> map = new HashMap<String, LabelledElement>();
for(AdaptedEntry adaptedEntry : adaptedEntries) {
map.put(adaptedEntry.key, adaptedEntry.value);
}
return map;
}
}
For reference, my solution is inspired by this link.
Apparently it's possible with EclipseLink JAXB (MOXy) implementation, which allows you annotate an interface variable providing a Factory class and method to be used when binding XML to Java, see this answer.
(edited to provide an example of this approach)
For instance, you instead of having an abstract class LabelledElement, have an interface LabelledElement:
public interface LabelledElement {
String getLabel();
}
and then have classes A and B implement it like this:
import javax.xml.bind.annotation.XmlElement;
public class A implements LabelledElement{
private String label;
#Override
#XmlElement(name="label")
public String getLabel() {
return label;
}
}
and Entity class annotated like this:
#XmlRootElement(name = "Entity")
#XmlAccessorType(XmlAccessType.NONE)
public class Entity {
#XmlElement(name = "Header")
private Header header;
#XmlRootElement
#XmlType(
factoryClass=Factory.class,
factoryMethod="createLabelledElement")
private LabelledElement labelledElement;
// constructors, getters, setters...
}
Then, as the answer I linked to suggests, you need a Factory class like:
import java.lang.reflect.*;
import java.util.*;
public class Factory {
public A createA() {
return createInstance(A.class);
}
public B createB() {
return createInstance(B.class);;
}
private <T> T createInstance(Class<T> anInterface) {
return (T) Proxy.newProxyInstance(anInterface.getClassLoader(), new Class[] {anInterface}, new InterfaceInvocationHandler());
}
private static class InterfaceInvocationHandler implements InvocationHandler {
private Map<String, Object> values = new HashMap<String, Object>();
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if(methodName.startsWith("get")) {
return values.get(methodName.substring(3));
} else {
values.put(methodName.substring(3), args[0]);
return null;
}
}
}
}

JAX-WS: XmlElementWrapper produces extra client code

I have some service with some WebMethod that returns object of Foo class:
public class Foo {
private List<Detail> detailList;
#XmlElement(name = "detail")
#XmlElementWrapper(name = "detailList")
public List<Detail> getDetailList() {
return detailList;
}
public void setDetailList(List<Detail> value) {
this.detailList = value;
}
public Foo() {
this.detailList = new ArrayList();
}
}
This code produces proper XML like:
<detailList>
<detail>
<key></key>
<value></value>
</detail>
<detail>
<key></key>
<value></value>
</detail>
<detailList/>
After building client JAR library it works OK.
But I really don't like the code I need to call to get the List:
foo.getDetailList().getDetail();
Because getDetailList() returns DetailList object.
How can I have getDetailList() method returning List without any changes in above XML?
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Root", propOrder = {
"detailList"
})
public class Foo {
#XmlElementWrapper(name = "detailList", required = true)
#XmlElement(name = "detail")
private List<Detail> detailList;
public List<Detail> getDetailList() {
return detailList;
}
public void setDetailList(List<Detail> value) {
this.detailList = value;
}
public Foo() {
this.detailList = new ArrayList();
}
}

Categories

Resources