How to unmarshal xml without mapping the root xml element? - java

I want to unmarshal a xml file containing a collection of data, like this
<Persons>
<Person>Jim</Person>
<Person>Tom</Person>
</Persons>
We know this can be done with two classess: Persons, Person using Castor , JAXB, or other frameworks.
But how to do without writing a collection class Persons ?

JAXB:
Iterate over subelements of the incoming XML (DOM, SAX, StAX - whatever API suits you best)
unmarshaller.unmarshal(node, Person.class)
There are also advanced techniques with programmaticaly created mappings.

Look for a way to tell Castor that you'd like it to generate a java.util.List of Person instances.
http://www.castor.org/how-to-map-a-list-at-root.html

You could use a StAX parser and do something like the following with any JAXB implementation (Metro, EclipseLink MOXy, Apache JaxMe, etc):
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
public class Demo {
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newFactory();
FileInputStream xml = new FileInputStream("input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr.nextTag(); // Advance to "Persons" tag
xsr.nextTag(); // Advance to "Person" tag
JAXBContext jc = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
List<Person> persons = new ArrayList<Person>();
while(xsr.hasNext() && xsr.isStartElement()) {
Person person = (Person) unmarshaller.unmarshal(xsr);
persons.add(person);
xsr.nextTag();
}
for(Person person : persons) {
System.out.println(person.getName());
}
}
}
input.xml
<Persons>
<Person>Jim</Person>
<Person>Tom</Person>
</Persons>
System Output
Jim
Tom
Person
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
#XmlRootElement(name="Person")
public class Person {
private String name;
#XmlValue
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Related

Unmarshall XML element that occurs more than once using JAXB

I am trying to unmarshal XML document into my model. The issue is that I have one XML element <additional_detail> that occurs more than once in the XML document. When I unmarshal the XML document, the JSON element has the value of the last occurrence of <additional_detail>. What I am trying to achieve is having them all comma-separated maybe.
Exampple of document
.
.
.
<additional_detail>
160cm
</additional_detail>
<additional_detail>
200KG
</additional_detail>
.
.
.
and I have in my java model:
#XmlElement(name = "additional_detail", namespace = NAME_SPACE)
private String additionalDetail;
hopefully below example can help
xml
<Example>
<detail>1</detail>
<detail>2</detail>
<detail>3</detail>
</Example>
code
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
public class JaxBUnmarshalExample {
public static void main(String[] args) {
try {
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Example.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Example que = (Example) jaxbUnmarshaller.unmarshal(file);
System.out.println(que.details);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
#XmlRootElement(name = "Example")
class Example {
List<String> details = new ArrayList<String>();
String detail;
#XmlElement(name = "detail")
public String getDetail() {
return detail;
}
/*
here adding items in list later list can be used
*/
public void setDetail(String detail) {
details.add(detail);
this.detail = detail;
}
}
As mentioned in the comment you should use the List to capture all the information which have the same tags in XML. Following is the example which shows the example of the similar xml:
XML:
<root>
<additional_detail>160cm</additional_detail>
<additional_detail>200KG</additional_detail>
</root>
Parent.class:
#XmlRootElement(name = "root")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
#XmlElement(name = "additional_detail")
List<String> additional_detail;
}
Main.class:
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("action.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Parent.class).createUnmarshaller();
final Parent parent = unmarshaller.unmarshal(xmlStreamReader, Parent.class).getValue();
System.out.println(parent.toString());
Marshaller marshaller = JAXBContext.newInstance(Parent.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(parent, System.out);
}
}
If in case you have the outer tags for those lists something like additional_details (observe the 's') then you can do something like this:
<root>
<additional_details>
<additional_detail>160cm</additional_detail>
<additional_detail>200KG</additional_detail>
</additional_details>
</root>
Parent.class:
#XmlRootElement(name = "root")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
#XmlElementWrapper(name = "additional_details")
#XmlElement(name = "additional_detail")
List<String> additional_detail;
}

JAXB unmarshal wrapped list

Is it possible to directly unmarshal a list of wrapped elements into a List<String> in JAXB?
For example I have a XML like:
<TEST>
<MIME_INFO>
<MIME>
<MIME_SOURCE>foo.png</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
</MIME>
<MIME>
<MIME_SOURCE>bar.png</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
</MIME>
</MIME_INFO>
</TEST>
So would it be possible to directly unmarshal this XML file into a List<String> only containing { "foo.png", "bar.png" }?
I know that you could create a class hierarchy with the correct annotations to perform this unmarshalling but i would like to have a code like:
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "TEST")
#XmlAccessorType (XmlAccessType.FIELD)
public class Info {
// -> what annotation to put here?
private List<String> infos;
public List<String> getInfos() {
return infos;
}
}
And the main file:
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class UnmarshallingJAXB {
public static void main(String[] args) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(Info.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Info info = (Info) jaxbUnmarshaller.unmarshal(new File("test.xml"));
// should output foo.png and bar.png
info.getInfos().forEach(System.out::println);
}
}
Is there any way to do that?
Apologies for answering so late, but this is the first hit on Google if search for related problems so it might help anyway.
Yes, it is possible, using
#XmlElementWrapper
in combination
#XmlElement
See this example for more detailed info.

Using JAXB&MOXy to convert JPA entities to XML and back again

I am stuck with (I guess) a pretty trivial problem considering MOXy. Converting a class like this example (pastebin) to XML is no problem, converting it back goes without any errors too. Though, fields that are referencing another (or more) Person, will result in a null value.
Is there any way to make this work without losing relationships? My guess is this is due to a reference by ID only, since JAXB has no way of knowing other existing objects. I have tried using #XmlInverseReference, though this resulted in an infinite loop on every try.
EclipseLink MOXy's #XmlInverseReference is used to solve the infinite loop problem. I will demonstrate below with an example based on your model:
Person
package forum15821738;
import java.io.Serializable;
import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Person implements Serializable {
private String name;
#XmlInverseReference(mappedBy="children")
private Person parent;
#XmlElementWrapper
#XmlElement(name="child")
private List<Person> children;
public Person getParent() {
return parent;
}
public List<Person> getChildren() {
return children;
}
// OTHER GETTERS AND SETTERS
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html)
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum15821738;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum15821738/input.xml");
Person person = (Person) unmarshaller.unmarshal(xml);
for(Person child : person.getChildren()) {
System.out.println(child.getParent());
}
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(person, System.out);
}
}
Input/Output
forum15821738.Person#5893a012
forum15821738.Person#5893a012
<?xml version="1.0" encoding="UTF-8"?>
<person>
<name>Jane</name>
<children>
<child>
<name>Bobbie</name>
</child>
<child>
<name>Sue</name>
</child>
</children>
</person>
For More Information
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html

Marshalling/unmarshalling fields to tag with attributes using JAXB

Let's say I have class Example:
class Example{
String myField;
}
I want to unmarshal it in this way:
<Example>
<myField value="someValue" />
</Example>
Is it possible to unmarshal object in such way using JAXB XJC? ( I know about XmlPath in EclipseLink, but can't use it).
You could leverage an XmlAdapter for this use case. In that XmlAdapter you will convert a String to/from an object that has one property mapped to an XML attribute.
XmlAdapter
package forum12914382;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MyFieldAdapter extends XmlAdapter<MyFieldAdapter.AdaptedMyField, String> {
#Override
public String unmarshal(AdaptedMyField v) throws Exception {
return v.value;
}
#Override
public AdaptedMyField marshal(String v) throws Exception {
AdaptedMyField amf = new AdaptedMyField();
amf.value = v;
return amf;
}
public static class AdaptedMyField {
#XmlAttribute
public String value;
}
}
Example
package forum12914382;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name="Example")
#XmlAccessorType(XmlAccessType.FIELD)
class Example{
#XmlJavaTypeAdapter(MyFieldAdapter.class)
String myField;
}
Demo
package forum12914382;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Example.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum12914382/input.xml");
Example example = (Example) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(example, System.out);
}
}
input.xml/Output
<Example>
<myField value="someValue" />
</Example>
Related Example
JAXB Element mapping
Yes, manually add the #XmlAttribute-Annotation or generate the classes from an XSD.

JAXB location in file for unmarshalled objects

I have some objects being unmarshalled from an XML file by JAXB. Is it possible to have JAXB tell me or somehow find out where in the XML file (line and column) each object comes from?
This information is available at some point, because JAXB gives it to me during schema validation errors. But I would like to have it available for validated objects too.
You could do this in JAXB by leveraging an XMLStreamReader and an Unmarshaller.Listener:
Demo
package forum383861;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.Unmarshaller.Listener;
import javax.xml.stream.Location;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
FileInputStream xml = new FileInputStream("src/forum383861/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
Unmarshaller unmarshaller = jc.createUnmarshaller();
LocationListener ll = new LocationListener(xsr);
unmarshaller.setListener(ll);
Customer customer = (Customer) unmarshaller.unmarshal(xsr);
System.out.println(ll.getLocation(customer));
System.out.println(ll.getLocation(customer.getAddress()));
}
private static class LocationListener extends Listener {
private XMLStreamReader xsr;
private Map<Object, Location> locations;
public LocationListener(XMLStreamReader xsr) {
this.xsr = xsr;
this.locations = new HashMap<Object, Location>();
}
#Override
public void beforeUnmarshal(Object target, Object parent) {
locations.put(target, xsr.getLocation());
}
public Location getLocation(Object o) {
return locations.get(o);
}
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<address/>
</customer>
Output
[row,col {unknown-source}]: [2,1]
[row,col {unknown-source}]: [3,5]
Customer
package forum383861;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Customer {
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address
package forum383861;
public class Address {
}
For More Information
http://blog.bdoughan.com/2011/08/using-unmarshallerlistener-to-capture.html
I'm afraid not. JAXB builds on top of a XML parser, this one will have built up a logical representation of your XML document forgetting the original string representation of your document.
The validation step is done while your string is still read in, so your parser is able to give you an error message telling you the position of the error. JAXB will only bypass that error message. But as soon as the XML is validated and parsed, only the logical representation will exist.

Categories

Resources