How to unmarshal nested dynamic xml without xsd? - java

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;
// ....
}

Related

How to add wrapper element to class during the JAXB marshalling

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;

JAXB unmarshall Mixed Content

I'm trying to parse existing XML files for a java application and some of the elements have mixed content occasionally as demonstrated below:
XML_STRING
<root>
<name>Michael</name>
<question>Text here</question>
</root>
XML_STRING_2
<root>
<name>Michael</name>
<question>Text here<measure>More Text</measure></question>
</root>
I Made the below classes to unmarshall this data.
Root Class
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root implements Serializable
{
private String name;
private Question question;
}
Question Class
#XmlAccessorType(XmlAccessType.NONE)
public class Question implements Serializable
{
#XmlValue
private String questionText;
private String measure;
}
I can't seem to get both the Text Here and More Text to be stored inside the Question class.
JAXBContext.newInstance(Root.class)
.createUnmarshaller()
.unmarshal(new ByteArrayInputStream(XML_STRING_2.getBytes("UTF-8")));
I'm printing the result of the above code snippet (with Lombok's #ToString() annotation added to Root and Question) for both XML_STRING and XML_STRING_2.
XML_STRING: Root(name=Michael, question=Question(questionText=Text here, measure=null))
XML_STRING_2:Root(name=Michael, question=Question(questionText=, measure=null))
I was able to get something I could work with by using #XmlMixed along with #XmlElementRefs inside Question and creating a Measure class.
#XmlAccessorType(XmlAccessType.NONE)
public class Question implements Serializable
{
#XmlMixed
#XmlElementRefs({
#XmlElementRef(name = "measure", type=Measure.class)
})
private List<?> content;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="measure") // If this is removed I get the error: "Invalid #XmlElementRef : Type `Measure` or any of its subclasses are not known to this context."
public class Measure
{
#XmlValue
private String value;
}
I'm now getting the below output, which i can use via instanceof checking the items inside content
Root(name=Michael, question=Question(content=[Text here]))
Root(name=Michael, question=Question(content=[Text here, Measure(value=More Text)]))
The Root file remained unchanged.

How to write POJO for Marshalling two list of objects?

<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>.

XML Mapping to Java Object

I have a Xml Like this
<entry>
<comboBox>
<name>xxx</name>
<details>sdfd</details>
</comboBox>
</entry>
In the other entry I have XML like this
<entry>
<numberField>
<name>xxx</name>
<details>sdfd</details>
</numberField>
</entry>
I want to map both comboBox and numberField to the same class in Java called Field
How do I annotate Java Fields in Entry Class?
In your Entry class you need to annotate the Java field with
#XmlElements
and list the individual element names there. Like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class Entry {
#XmlElements({
#XmlElement(name = "comboBox", type = Field.class),
#XmlElement(name = "numberField", type = Field.class)
})
private Field field;
}
I have added type = Field.class in the annotation above only for clarity.
In your case you can omit it. Then JAXB will pick up Field from the property type decaration , which has the same desired effect.
The Field class can be straight-forward like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class Field {
#XmlElement
private String name;
#XmlElement
private String details;
}
I think you should make two sub classes of an object which has the common annoted fields.
Each sub class just has to define jaxb #XmlRootElement (number field or combobox)

java Map to XML through XSD

First. Sorry for bad english.
I want to make some "common" transformation of Map to XML according to given XSD in that way:
key of the Map will be equal to tag name in XML
tag names in XML will not be duplicated in different nodes (levels)
value in Map can contain for example List of Map that represent repeatable tags in the node
created xml have to accord an xsd.
etc.
So I am looking for a competent way to realize that.
Is there anybody who worked with similar tasks and can help me?
Any advise will appreciated. Thanks in advance!
P.S. Example.
Map:
"fname" : "Asdf"
"lname" : "Fdsa"
"cars" : "car" {"car1", "car2", "car3"}
XML:
<fname>Asdf</fname>
<lname>Fdsa</lname>
<cars>
<car>car1</car>
<car>car2</car>
<car>car3</car>
</cars>
First, you need one single root element. This is the requirement of XML syntax.
Now you can use JAXB. Define you class Data:
#XmlType
public class Data {
private String fname;
private String lname;
private Collection<String> cars;
// getters
public String getFname() {
return fname;
}
public String getLname() {
return lname;
}
#XmlElementWrapper(name = "cars")
#XmlElement(name = "car")
public String getCars() {
return cars;
}
// setters.....
}
Now your can create instance of this class instance, call all setters to fill the data and then call:
JAXBContext ctx = JAXBContext.newInstance("com.yourpackage");
Marshaller m = ctx.createMarshaller();
m.marshal(data, System.out);
And you will see your data serialized as XML on STDOUT.
To parse XML back say:
JAXBContext ctx = JAXBContext.newInstance("com.panpwr.api.model.deployment");
Unmarshaller unmarshaller = ctx.createUnmarshaller();
Data data = (Data)unmarshaller.unmarshal(in); // in is the input stream for XML
// now your instance of data is populated from XML

Categories

Resources