JAXB - can class containment be flattened when marshalling to XML? - java

Say, I have two classes:
#XmlRootElement
class A {
#XmlElement
String propertyOfA;
#XmlElement
B b;
}
class B {
#XmlElement
String propertyOfB;
}
JAXB returns an XML formatted in the according way:
<a>
<propertyOfA>valueA</propertyOfA>
<b>
<propertyOfB>valueB</propertyOfB>
</b>
</a>
My question is how to flatten the hierarchy in the XML? So that I have:
<a>
<propertyOfA>valueA</propertyOfA>
<propertyOfB>valueB</propertyOfB>
</a>
Can this be done with annotations?
At the moment I am thinking to create a kind of wrapper class for A, that would have fields built the way I want to see them in the XML. Is there a better way?

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
You could use MOXy's #XmlPath extension to map this use case:
import java.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
class A {
#XmlElement
String propertyOfA;
#XmlPath(".")
B b;
}
For More Information
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html
http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

It's been a while for me, but let me give it a crack:
#XmlRootElement
class A {
#XmlElement
String propertyOfA;
#XmlElement(name="propertyOfB")
B b;
}
#XmlType(name="")
class B {
#XmlValue
String propertyOfB;
}
Edit: disclaimer- I havn't compiled or tried this. But I believe it's how you do it.

Take a look to that question and the solution proposed: Spring RESTful client: root tag exception
Very helpful to solve this kind of problem.

An alternative approach (with standard JAXB) would be using #XmlJavaTypeAdapter. This way you could adapt the object hierarchy with the drawback of having to write the code that translates the object hierarchy into the adapted new class.
In your example however it wouldn't work as you would have to adapt class A which is your root. If however the hierarchy was deeper and you needed to make the adaptation one level lower than the root, then there wouldn't be a problem. A suggestion that might be useful is to write the code in the adapted entity, making it like a delegate to the object hierarchy, rather than the adapter, which would then be very thin.

Related

JAXB ordering multiple properties

In my project, I would like to achieve specific order of properties in XML. In java object is represented as:
public class Plan {
private List<Act> act;
private List<Leg> leg;
...
An output object should look similar to this one:
<plan>
<act x="-25000" y="0"/>
<leg mode="car">
...
</leg>
<act x="10000" y="0"/>
<leg mode="car">
...
</leg>
</plan>
Is JAXB able to set up specific order for such case where I need to put items in order:
act.get(0)
leg.get(0)
act.get(1)
leg.get(1)
...
..
.
I know JAXB is able to save specific order of parameters like firstly act, then all legs, using #XmlType (propOrder={"prop1","prop2",..."propN"}) but it is not the case of this project as the 3rd party application which reads this xml's read them in pairs and propOrder would print them one by another.
Ok, I approach the problem from a different side and I solved it... Previously I thought it was a sorting problem - in fact, it is a problem with Java POJO class construction and JAXB annotations.
The solution for that is to use
#XmlElements({
#XmlElement(name="leg", type=Leg.class),
#XmlElement(name="act", type=Act.class)
})
#XmlElementWrapper(name="plan")
public List<Plan> getPlan() {
return plan;
}
and then items must be put one by another.
more details can be found in this answer:
Marshalling a List of objects implementing a common interface, with JaxB

JAXB Without Defining Class or Annotations

I would like to use JAXB to read and write only a few parts of a very large XML. I would like to be able to do this without having to define a root object class for every element and attribute in the XML. The example below outlines what I need:
I have the XML
<A>
<B/>
<C/>
<D/>
</A>
I would like to use JAXB to get two functions
public String getC() {
...
return C
}
public void writeC(String C) {
... // replaces C value with the paramter C inside the XML
}
Without having to define a new class A with the annotations for B, C, and D.
How can I do this with JAXB? Is there a faster / more efficient way to achieve what I am trying to do than JAXB or a simple File Reader and Writer?
The purpose of this is to use a GUI to load and edit config settings that are stored in an XML file. Thank you.
I managed to solve this by using StAX instead. It offered complete flexibility for me to pick out which tags and which attrbitues I needed.
There is a project called EclipseLink MOXy that could solve this type of problem using JAXB. It allows you to map an existing java bean to or from xml and define exceptions from default JAXB using xml or json for the mapping description.

JAXB add grouping element to a list, .NET like

I'm working with a xml file that is generated and used in a .NET application.
I need to deserialize this file in Java and chose to use JAXB.
However, after trying to create the matching class I figured out one major difference.
When having a
#XmlRootElement
public class SomeClass{
List<NestedClass> classes;
}
I get the following structure:
<SomeClass>
...
<NestedClasses>
...
</NestedClasses>
<NestedClasses>
...
</NestedClasses>
</SomeClass>
As oppesed to the required existing structure:
<SomeClass>
...
<NestedClasses>
<NestedClass>
...
</NestedClass>
<NestedClass>
...
</NestedClass>
</NestedClasses>
</SomeClass>
Is there is a way to get my required structure without adding a class that will only contain the list of the nested class?
You will need to have a class that wraps the collection. To get the desired mapping, you can use the #XmlElementWrapper annotation to specify that a grouping element should be used. Then you can use the #XmlElement annotation to specify the name of the element that represents the items in the collection:
#XmlRootElement
public class SomeClass{
#XmlElementWrapper
#XmlElement(name="NestedClass")
List<NestedClass> classes;
}
For More Information
http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html

How to map an XML to Java objects by XPaths?

Given the XML example:
<fooRoot>
<bar>
<lol>LOLOLOLOL</lol>
</bar>
<noob>
<boon>
<thisIsIt></thisIsIt>
</boon>
</noob>
</fooRoot>
Which should be mapped to:
class MyFoo {
String lol;
String thisIsIt;
Object somethingUnrelated;
}
Constraints:
XML should not be transformed, it is provided as a parsed org.w3c.dom.Document object.
Class does not and will not map 1:1 to the XML.
I'm only interested to map specific paths of the XML to specific fields of the object.
My dream solution would look like:
#XmlMapped
class MyFoo {
#XmlElement("/fooRoot/bar/lol")
String lol;
#XmlElement("/noob/boon/thisIsIt")
String thisIsIt;
#XmlIgnore
Object somethingUnrelated;
}
Does anything likewise exists? What I've found either required a strict 1:1 mapping (e.g. JMX, JAXB) or manual iteration over all fields (e.g. SAX, Commons Digester.)
JiBX binding definitions come the nearest to what I'm lokking for. However, this tool is ment to marshall/unmarshall complete hierarchy of Java objects. I only would like to extract parts of an XML document into an existing Java bean at runtime.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
You can do this with MOXy:
#XmlRootElement(name="fooRoot")
class MyFoo {
#XmlPath("bar/lol/text()")
String lol;
#XmlElement("noob/boon/thisIsIt/text()")
String thisIsIt;
#XmlTransient
Object somethingUnrelated;
}
For More Information
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html
http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
Try XStream. It's super easy. Hope it helps! I don't have time now for a full example :)
One option could be write a custom annotation which will take the XPath expression as input and do the bindings.

parse google geocode with xstream

I'm using Java and XStream to parse a google geocode request over http. My idea is to have an Address class with all the geocode attr's (ie. lat/long, city, provice/state etc) but I'm having problems parsing the xml with xstream.
The google response is similar to this:
<?xml version="1.0" encoding="UTF-8" ?>
<kml xmlns="http://earth.google.com/kml/2.0"><Response>
<name>98 St. Patrick St, Toronto</name>
<Status>
<code>200</code>
<request>geocode</request>
</Status>
<Placemark id="p1">
<address>98 St Patrick St, Toronto, ON, Canada</address>
<AddressDetails Accuracy="8" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"> <Country><CountryNameCode>CA</CountryNameCode><CountryName>Canada</CountryName><AdministrativeArea><AdministrativeAreaName>ON</AdministrativeAreaName><Locality><LocalityName>Toronto</LocalityName><Thoroughfare><ThoroughfareName>98 St Patrick St</ThoroughfareName></Thoroughfare><PostalCode><PostalCodeNumber>M5T</PostalCodeNumber></PostalCode></Locality></AdministrativeArea></Country></AddressDetails>
<ExtendedData>
<LatLonBox north="43.6560378" south="43.6497426" east="-79.3864912" west="-79.3927864" />
</ExtendedData>
<Point><coordinates>-79.3896388,43.6528902,0</coordinates></Point>
</Placemark>
</Response></kml>
That doesn't show up very well, but the meat of the code is in the AddressDetails tag.
Anyway, I'm new to Java and XStream so the API terminology is a bit confusing for me. I just need to be able to write some mapper that maps all these tags (ie. CountryName) to an attribute within my Address object, (ie. address.country = blah) The address object will be pretty simple, mainly just strings for country name etc and floats for lat/long.
The docs and example just show straight mapping where each xml tag maps directly to the attribute of the same name of the object. In my case however, the tags are named different than the object attr's. A quick point in the right direction is all I'm looking for really.
I've used XStream in several projects. Unfortunately, your problem isn't really what XStream is designed to solve. You might be able to use its converter mechanism to achieve your immediate goal, but you'll run into limitations. In a nutshell, XStream isn't designed to do conversion of Tree Structure A into Tree Structure B -- it's purpose is to convert from a Java domain model into some reasonable XML. XStream is a great tool when you don't care much about the details of the XML produced. If you care more about the XML than the Java objects, look at XMLBeans -- the Java is ugly, but it's incredibly schema-compliant.
For your project, I'd run the Google XML schema through XML beans, generate some Java that will give you a more literate way of hand-coding a converter. You could use a raw DOM tree, but you'd have code like myAddress.setStreet(root.getFirstChild().getAttribute("addr1"))). With XML beans, you say things like myAddress.setStreet(googleResult.getAddress().getStreetName();
I'd ignore JAXB as it's attempt to separate interface from implementation adds needless complexity. Castor might be a good tool to consider as well, but I haven't used it in years.
In a nutshell, there aren't a lot of good Object-to-Object or XML-to-Object converters that handle structure conversion well. Of those I've seen that attempt declarative solutions, all of them seemed much more complicated (and no more maintainable) than using XStream/XmlBeans along with hand-coded structure conversions.
Would it be possible to define a separate class specifically for dealing with XStream's mapping? You could then simply populate your AddressDetails object by querying values out of this other object.
I've ended up just using xpath and populating my own address object manually. Seems to work fine.
Have you tried with json format? It should be the same but you'll need to set a com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver as the driver for XStream
You could use EclipseLink JAXB (MOXy) to do this:
package com.example;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="kml")
public class Address {
private String country;
#XmlPath("Response/Placemark/ns:AddressDetails/ns:Country/ns:CountryName/text()")
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
and
#javax.xml.bind.annotation.XmlSchema(
namespace = "http://earth.google.com/kml/2.0",
xmlns = {
#javax.xml.bind.annotation.XmlNs(
prefix = "ns", namespaceURI ="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.example;
A full example is available here:
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html

Categories

Resources