I am trying to create XML using the Moxy Marshalling approach. Everything seems to be working fine except for one small thing. Basically, I would like to know how to add wrapper element to the class itself during the marshalling.
As we are aware we can add #XmlPath("test/name/text()") to add a wrapper to any of the String elements. Also for collection we can make use of #XmlElementWrapper(name="languages"), #XmlElement(name="language"). But these are for the fields within the class. How can I add the wrapper element to the class itself which is being marshalled?
I have the following class:
#XmlRootElement(name = "customer")
#XmlType(name="customer",propOrder={"name","languages"})
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer{
#XmlPath("extension/name/text()")
private String name;
#XmlElementWrapper(name = "languages")
#XmlElement(name = "language")
private List<String> languages;
//Getter, Setter and other constuctors
}
This will produce the output XML something like this:
<customer>
<extension>
<name>Hello</name>
</extension>
<languages>
<language>English</language>
<language>UK English</language>
</languages>
</customer>
However, I would like to add the wrapper element to event the whole class so that output would look something like this: (Notice the whole customer is wrapper within classWrapper)
<classWrapper>
<customer>
<extension>
<name>Hello</name>
</extension>
<languages>
<language>English</language>
<language>UK English</language>
</languages>
</customer>
</classWrapper>
I tried adding the #XmlPath and #XmlElementWrapper to the Customer class but it throws an error as it can be added only to the fields within the class but not to the whole class.
Following is my Main class which would be used for marshalling:
public class HelloWorld{
public static void main(String []args){
Customer customer = new Customer();
List<String> languages = new ArrayList<String>();
languages.add("English");
languages.add("UK English");
customer.setName("Hello");
customer.setLanguages(languages);
JAXBContext context = JAXBContext.newInstance(Customer.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(customer,System.out);
}
}
I am aware that I can write another wrapper class add the custom field to it then use that wrapper class for the marshalling. Actually, I am doing the same as of now. However, trying to find if there is some Moxy approach of doing this similar to how we have #XmlPath and #XmlElementWrapper
Can someone please suggest some form of Moxy Jaxb related approach for achieving the wrapper element to the whole class?
This is a temporary workaround that works as expected for me. Posting the answer here so it can be helpful to someone in the future.
Change the #XmlRootElement("customer") to #XmlRootElement("classWrapper"). So you will get the classWrapper as the outer element.
Then change all the element within the Customer class using the #XmlPath so that all element go under the Customer tag. So overall customer.class would look something like this:
#XmlRootElement(name = "classWrapper")
#XmlType(name="customer",propOrder={"name","languages"})
#XmlAccessorType(XmlAccessType.FIELD)
#Getter
#Setter
public class Customer{
#XmlPath("customer/extension/name/text()")
private String name;
#XmlPath("customer/languages/language/text()")
private List<String> languages;
//Getter, Setter and other constuctors
}
Just a tip while using the #XmlPath:
do not use text() if its not simple type such as String,Date, etc type.
For example if the element is of Custom type with List then do not use /text()
#XmlPath("extension/elements/element")
List<myType> elements;
This will add extension -> elements -> element then content.
<extension>
<elements>
<element></element>
<element></element>
......
</elements>
</extension>
If the elements are of String type then you have to use text()
#XmlPath("extension/elements/text()")
List<String> elements;
Related
<MyRoot>
<Person>
<Name>Joe</Name>
<Age>20</Age>
</Person>
<Address>
<HouseName>Joe</HouseName>
<Place>Delhi</Place>
</Address>
<Person>
<Name>James</Name>
<Age>21</Age>
</Person>
<Address>
<HouseName>Joe</HouseName>
<Place>Mumbai</Place>
</Address>
</MyRoot>
From above xml, you can see that person and address tag is not wrapped in a wrapper tag. I want to generate an xml using JAXB in same format. I don't know how to do that without a wrapper tag.
Are you looking into creating java object that contains a list of Persons and a list of addresses?
public class MyRootObject {
private List<Person> persons;
private List<Address> addresses;
}
If the above is what you intend on doing then note that the XML object will change...
If you really want to preserve a mixed sequence like <Person> <Address> <Person> <Address> (as you say in your comment to #Vankuisher's answer),
then you need to keep the Persons and Addresses not in 2 separate Lists,
but together within the same List.
For that to work Person and Address must be subclasses of a common superclass
(e.g. class Person extends Item and class Address extends Item).
Then you use an #XmlElements annotation to define the mapping between
XML element names and Java classes:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "MyRoot")
public class MyRoot {
#XmlElements({
#XmlElement(name = "Address", type = Address.class),
#XmlElement(name = "Person", type = Person.class)
})
private List<Item> items;
// public getters and setters (omitted here for brevity)
}
When marshalling such a MyRoot object you will get an XML output
with the same sequence of items as given within the List<Item>.
I'm able to unmurshal a single occurrence of a dynamic xml instantiating java classes (a wrapper and an adapter), but I don't understand how to extend this mechanism to a list of occurrences; the xml is like
<ALLRECORDSDATASET>
<RECORD>
<FIELD_0001>000248031</FIELD_0001>
<FIELD_0022>A</FIELD_0022>
<FIELD_0031>0</FIELD_0031>
<FIELD_0033>1994-01-01</FIELD_0033>
</RECORD>
<RECORD>
<FIELD_0001>000248056</FIELD_0001>
<FIELD_0027>ABC</FIELD_0027>
<FIELD_0037>DEF</FIELD_0037>
<FIELD_0040>1994-01-01</FIELD_0040>
</RECORD>
</ALLRECORDSDATASET>
and I can get the last values of RECORD (having a Record class containing a Fields class using #XmlAnyElement annotation), but I can't get all the RECORD list.
Can anyone help me? Thanks
Thats one way of doing that
#XmlRootElement(name = "ALLRECORDSDATASET")
#XmlAccessorType(XmlAccessType.NONE)
public class DataSet {
#XmlElements({ #XmlElement(name = "RECORD", type = Record.class) })
private List<Record> records;
}
#XmlAccessorType(XmlAccessType.NONE)
public class Record {
#XmlElement(name = "FIELD_0001")
private String field;
// ....
}
I am trying to wrap 2 Classes, that are built following the Composite pattern, with JAXB.
The whole thing i built like this:
public interface ICriterion {String somemethod(String arg);}
#XmlRootElement(name = "criterion")
#XmlAccessorType(XmlAccessType.FIELD)
public class Criterion implements ICriterion {
#XmlElement String name;
#XmlElement String data;
//getter, setter, somemethod(), Criterion(), Criterion(String, String)
}
#XmlRootElement(name = "criteria")
#XmlAccessorType(XmlAccessType.FIELD)
public class Criteria implements ICriterion {
#XmlAnyElement(lax=true)
private final List<ICriterion> criteria;
//getter, setter, somemethod(),
Criteria(){criteria = new ArrayList<>();}
Criteria(List<ICriterion>)...
}
i took this solution from this answer
It works fine for marshalling - i can provide a Criteria, which contain a list of Criteria and Criterions, it gets marshaled to xml correctly.
What does not work however is unmarshaling a string to an Object
String request =
<criteria>
<criteria>
<criterion>
<name>number1</value>
<data>123123</token>
</criterion>
<criterion>
<name>number2</value>
<data>1223323</token>
</criterion>
</criteria>
<criterion>
<name>number3</value>
<data>1212</token>
</criterion>
</criteria>
JAXBContext ctx = JAXBContext.newInstance(Criteria.class, Criterion.class);
StringReader reader = new StringReader(request);
Criteria wrapped = (Criteria) ctx.createUnmarshaller().unmarshal(reader);
gives:
java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
I've tried other ways that are avaiable through online sources like:
#XmlElementRefs({ #XmlElementRef(type=Criterion.class), #XmlElementRef(type=Criteria.class)})
#XmlElements({#XmlElement(type=Criteria.class),#XmlElement(type=Criterion.class)})
which do not give an error, but result in the List being empty instead. The solution to this problem, is using #XmlAnyElement(lax=true), which does not work for me.
My first guess was, that somehow the List is not initialized correctly in my Constructor and JAXB uses some kind of fixed size list.
My countermeasure was to explicitly initialize the list in the no-Args constructor
Criteria(){criteria = new ArrayList<>();}
The problem also is not solved by explicitly making "critera" an ArrayList<> instead of List<>.
What am I missing?
I'm trying to unmarshal the following XML using Jackson 2.4.0 XmlMapper:
<root>
<a/>
<b/>
</root>
...and the following POJO
class Root {
#JacksonXmlElementWrapper(useWrapping = false)
#JsonSubTypes({
#JsonSubTypes.Type(name = "a", value = POJO_A.class),
#JsonSubTypes.Type(name = "b", value = POJO_B.class)
})
public final List<AbstractPOJO> objects = new ArrayList<>();
}
I've also tried with JAXB #XmlElements annotations with the same result, which is:
Unrecognized field "a" (class Root), not marked as ignorable (1 known properties: "objects"])
So it seems that Jackson thinks my list is called "objects" instead of "a" and "b". Normally I fix that by using #JsonProperty("newName") but in this case I expected that to be handled by the #JsonSubtypes or #XmlElements annotations.
As I cannot modify the input XML, is there anything else I can do, either with Jackson, Jackson XML or JAXB annotations?
Update: Forgot to say, the problem is deserializing to the same collection (because I need to keep the order and they can be mixed). Doing it in separate fields works just fine.
When I was trying peeskillet answer and changed Jackson for JAXB unmarshaller, using #XmlElements/#XmlElementRefs worked (#XmlAnyElement did not for some reason, I was getting a list of ElementNSImpl instead of my own classes).
It will be nice to have this working with Jackson, but in the meantime, this is the way to make this work.
UPDATE:
What I did was:
class Root {
#XmlElements({
#XmlElement(name = "a", type = PojoA.class),
#XmlElement(name = "b", type = PojoB.class)
})
public final List<AbstractPOJO> objects = new ArrayList<>();
}
I also added a #XmlRootElement(name = "a") to PojoA, but I think it's not used when you have #XmlElements
"As I cannot modify the input XML, is there anything else I can do, either with Jackson, Jackson XML or JAXB annotations?"
I'm not too familiar with Jackson's Xml features. But with the JAXB Unmarshaller and a little change to your annotations, this can be achieved.
For your List<AbstractPojo> you can use the #XmlAnyElement(lax = true).
With lax = true:
If true, when an element matches a property marked with XmlAnyElement is known to JAXBContext (for example, there's a class with XmlRootElement that has the same tag name, or there's XmlElementDecl that has the same tag name), the unmarshaller will eagerly unmarshal this element to the JAXB object, instead of unmarshalling it to DOM
Which basically means that if we annotate PojoA and PojoB with #XmlRootElement and pass the element name as the name attribute (#XmlRootElement(name = "a")), by definition, this should work.
Let's give it a shot:
public abstract class AbstractPojo {
// note this class is not annotated. It will be known in the context
// as we're explicitly using the type int the Root class
}
#XmlRootElement(name = "a")
public class PojoA extends AbstractPojo {
}
#XmlRootElement(name = "b")
public class PojoB extends AbstractPojo {
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "root")
public class Root {
#XmlAnyElement(lax = true)
protected List<AbstractPojo> objects;
public List<AbstractPojo> getObjects() {
if (objects == null) {
objects = new ArrayList<>();
}
return this.objects;
}
}
Using the following xml file to test
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a/> <b/> <b/> <a/> <a/> <b/> <b/>
</root>
And the following test program
public class JaxbTest {
private static final String FILE_NAME = "test.xml";
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
File f = new File(FILE_NAME);
Root root = (Root)unmarshaller.unmarshal(f);
List<AbstractPojo> list = root.getObjects();
for (AbstractPojo p : list) {
System.out.print( p instanceof PojoA ? "a " : "b ");
}
System.out.println();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
We get the result we're looking for.
a b b a a b b // along with the xml file content we marshal for testing
Here are some good resources:
JAXB tutorial
Oracle JAXB tutorial
#XmlAnyElement
Blaise Doughan's blog is always a good resource
UPDATE
Ok I see why you were getting ElementNSImpl. This doesn't work for me, I'm getting ElementNSImpl objects. Do you know what's happening?
Yea I see what's happening. Firstly, I compiled an xsd with xjc, and it created an ObjectFactory for me, which declared the elements. That's why it way working for me.
If you don't do this, then you should explicitly put the PojoA and PojoB into the context.
JAXBContext.newInstance(Root.class, PojoA.class, PojoB.class);
I need to map some legacy XML I can't change. There are several elements that have hundreds attributes exactly the same as some other elements. The attributes all have the same name postfixed with a number. So XML might look like this:
<someElement custom1="..." custom2="..." custom78=".."/>
<anotherElmenent custom1="..." custom45="..."/>
A solution that "works" is to create a base class like so:
#XmlAccessorType(FIELD)
public class LotsaCustomIds
{
#XmlAttribute
private String custom1;
#XmlAttribute
private String custom2;
...
}
#XmlType
public class SomeElement extends LotsaCustomIds
{
....
}
But it's a shame to use inheritence here, especially since Java only has single inheritence. What I'd like to do is something like the way JPA/Hibernate do embedded objects, like:
#XmlType
public class SomeElement
{
#EmbeddedAttributes
private LotsaCustomIds customIds;
....
}
Anyway to do this?
Note: I'm the EclipseLink JAXB (MOXy) lead.
You could use MOXy's #XmlPath extension to map this use case. When you use it as #XmlPath(".") then it will pull the contents of the child object (LotsaCustomIds) into the parent object (SomeElement).
#XmlType
public class SomeElement
{
#XmlPath(".")
private LotsaCustomIds customIds;
....
}
Related Information from my Blog
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html