JaxB to read class hierarchy - java

Just extending Parsing class hierarchy using JaxB question.
Want to read following xml file using JaxB
<IMPORT>
<TABLE NAME="USER">
<ROW>
<USER_ID>1</USER_ID>
<ROW_VERSION>1</ROW_VERSION>
<USER_NAME>Navnath</USER_NAME>
<LOGIN>Navnath</LOGIN>
<LOGIN_PASSWORD>Navnath</LOGIN_PASSWORD>
</ROW>
<ROW>
<USER_ID>2</USER_ID>
<ROW_VERSION>1</ROW_VERSION>
<USER_NAME>Kumbhar</USER_NAME>
<LOGIN>Kumbhar</LOGIN>
<LOGIN_PASSWORD>Kumbhar</LOGIN_PASSWORD>
</ROW>
</TABLE>
<TABLE NAME="WORK">
<ROW>
<WORK_ID>1</WORK_ID>
<WORK_NAME>Work1</WORK_NAME>
<ROW_VERSION TYPE="N">1</ROW_VERSION>
</ROW>
<ROW>
<WORK_ID>2</WORK_ID>
<WORK_NAME>Work2</WORK_NAME>
<ROW_VERSION TYPE="N">1</ROW_VERSION>
</ROW>
</TABLE>
<TABLE> ... </TABLE>
<TABLE> ... </TABLE>
<TABLE> ... </TABLE>
</IMPORT>
You can see in above xml file is column names are different in each table. I want to insert this data in database. I try to create class hierarchy for this, But I don't know how to do this. My ROW class will contains diffrent xml element per table and this is the area which I am not able to configure. Please suggest.

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You could leverage MOXy's #XmlDescriminatorNode/#XmlDescriminatorValue extension for this use case (see: http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-moxy-extension.html).
Import
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="IMPORT")
#XmlAccessorType(XmlAccessType.FIELD)
public class Import {
#XmlElement(name="TABLE")
private List<Table> tables;
}
Table
The #XmlDescriminatorNode annotation is used to specify the XML attribute that will be used to indicate which subclass will be instantiated. A JAXB implementation can't pull in the subclasses of a class via reflection, we will use the #XmlSeeAlso annotation to reference them.
import javax.xml.bind.annotation.XmlSeeAlso;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
#XmlDiscriminatorNode("#NAME")
#XmlSeeAlso({UserTable.class, WorkTable.class})
public abstract class Table {
}
UserTable
The #XmlDescriminatorValue annotation is used to specify the value of the NAME attribute that corresponds to a particular subclass.
import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
#XmlDiscriminatorValue("USER")
#XmlAccessorType(XmlAccessType.FIELD)
public class UserTable extends Table {
#XmlElement(name="ROW")
private List<UserRow> rows;
}
UserRow
import javax.xml.bind.annotation.XmlElement;
public class UserRow {
#XmlElement(name="USER_ID")
private int userID;
#XmlElement(name="USER_NAME")
private String userName;
}
WorkTable
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
#XmlDiscriminatorValue("WORK")
public class WorkTable extends Table {
}
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
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Import.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum15741264/input.xml");
Import result = (Import) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(result, System.out);
}
}
input.xml/Output
Below is the input to and output from running the demo code.
<?xml version="1.0" encoding="UTF-8"?>
<IMPORT>
<TABLE NAME="USER">
<ROW>
<USER_ID>1</USER_ID>
<USER_NAME>Navnath</USER_NAME>
</ROW>
<ROW>
<USER_ID>2</USER_ID>
<USER_NAME>Kumbhar</USER_NAME>
</ROW>
</TABLE>
<TABLE NAME="WORK"/>
</IMPORT>
Alternative Solutions
Alternatively using only the standard JAXB APIs you could try the following approach using an XmlAdapter
http://blog.bdoughan.com/2012/01/jaxb-and-inhertiance-using-xmladapter.html

Related

JAXB marshalling vs. XSD schema generation - "desynchronize" it

For given structure of classes (skipped annotations and so on):
class A {
public B getObjectB {}
}
class B {
public String getHello { return " world"; }
}
I have no problems with generating correct XSD and XML output:
<A>
<B>
<hello>world</hello>
</B>
</A>
The thing is, I need to break it a little: first of all, XSD should remain as is - full. But while marshalling of A, I need to get somtething like this:
<A>
someStringForA
</A>
(so B is rendered as some calculated String). At the same time, while marshalling B (as a root), I still need to get "normal" output.
I tried with XmlAdapters, but using #XmlJavaTypeAdapter change the XSD too. Setting adapters through Marshaller.setAdapter(...) apparently (http://stackoverflow.com/questions/6110757/jaxb-xml-adapters-work-via-annotations-but-not-via-setadapter/6112149#6112149) will not work.
Some kind of solution would be, if there were possibility to "turn off" the #XmlJavaTypeAdapter (XSD is generated "manually" by JUnit, some switch or even hack is allowed)
Thanks for any help!
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Alternate Mapping - oxm.xml
If you are using MOXy as your JAXB provider you could use an external mapping file to provide an alternate mapping for your domain model (see: http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html).
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum13843624">
<java-types>
<java-type name="A">
<java-attributes>
<xml-value java-attribute="objectB"/>
</java-attributes>
</java-type>
<java-type name="B">
<java-attributes>
<xml-value java-attribute="hello"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Java Model
A
import javax.xml.bind.annotation.*;
#XmlRootElement(name="A")
class A {
private B b;
#XmlElement(name="B")
public B getObjectB() {
return b;
}
public void setObjectB(B b) {
this.b = b;
}
}
B
import javax.xml.bind.annotation.XmlElement;
class B {
#XmlElement
public String getHello() {
return " world";
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file named 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 Code
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
B b = new B();
A a = new A();
a.setObjectB(b);
JAXBContext jc = JAXBContext.newInstance(A.class);
marshal(jc, a);
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum13843624/oxm.xml");
JAXBContext jc2 = JAXBContext.newInstance(new Class[] {A.class}, properties);
marshal(jc2, a);
}
private static void marshal(JAXBContext jc, A a) throws Exception {
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(a, System.out);
}
}
Output
Below is the output from running the demo code. Note how the same object graph is marshalled two different ways.
<?xml version="1.0" encoding="UTF-8"?>
<A>
<B>
<hello> world</hello>
</B>
</A>
<?xml version="1.0" encoding="UTF-8"?>
<A> world</A>

Xstream or Jaxb schema support

I have xml like this
<abc:city>
<def:cityname />
<xyz:postalTown>
Sacramento
</xyz:postalTown>
</abc:city>
<abc:city>
<def:cityname />
<pqr:postalTown>
Sacramento
</pqr:postalTown>
</abc:city>
Can xstream handle these namespaces like 'abc' in <abc:city>
Also namespace for <pqr:postalTown> can be changed as I am unaware of the response coming. How can this be handled dynamically through xstream.
If this is impossible in xstream; can it be handled using jaxb?
EDIT:
My class will be City:
Class City{
String cityName;
String postalTown;
}
How can I map above xml to City class as tags contain prefixes?
UPDATE
If the prefixes do not correspond to namespace declarations, then you could use the approach from the answer I linked below from a related question:
https://stackoverflow.com/a/11970622/383861
NOTE ABOUT NAMESPACE QUALIFICATION
The prefixes used don't come into play in terms of object-to-XML mapping. As long as the the xyz and pqr prefixes correspond to the same namespace you will be fine with any object-to-XML solution that supports namespaces.
Even though the following documents contain different prefixes they have the same namespace qualification.
Document #1
<abc:city xmlns:abc="ABC" xmlns:def="DEF" xmlns:ghi="XYZ">
<def:cityName/>
<ghi:postalTown>
Sacramento
</ghi:postalTown>
</abc:city>
Document #2
<jkl:city xmlns:jkl="ABC" xmlns:mno="DEF" xmlns:pqr="XYZ">
<mno:cityName/>
<pqr:postalTown>
Sacramento
</pqr:postalTown>
</jkl:city>
JAXB AND NAMESPACES
Below is how you would map your City class to the XML documents above. Note how it is the namespace URI and not the prefix that is specified on the #XmlRootElement and #XmlElement annotations.
package forum11932402;
import javax.xml.bind.annotation.*;
#XmlRootElement(namespace="ABC")
public class City {
#XmlElement(namespace="DEF")
String cityName;
#XmlElement(namespace="XYZ")
String postalTown;
}
Below is some information on JAXB and namespaces:
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html
DEMO CODE
The following demo code can be used to unmarshal either of the XML documents I have posted earlier in this answer.
package forum11932402;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(City.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11932402/input.xml");
City city = (City) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(city, System.out);
}
}
Below is the output from running the demo code. The JAXB implementation has assigned new prefixes. The cityName element is still namespace qualified, it just corresponds to the default namespace which was declared as xmls="DEF".
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:city xmlns="DEF" xmlns:ns2="XYZ" xmlns:ns3="ABC">
<cityName></cityName>
<ns2:postalTown>
Sacramento
</ns2:postalTown>
</ns3:city>

Update partial XML mapping to bean

I need to map an xml file subset of nodes to a Java Bean.
For example map
<data>
<field1>Value</field1>
<field2>Value</field2>
<field3>Value</field3>
<field4>Value</field4>
<field5>Value</field5>
</data>
to
public class DataBean {
private String field2;
private String field5;
// ...getter/setter
}
then manipulate the bean and update the source xml file without loosing elements that are not mapped.
How can I use to do it?
What library?
Thanks for help,
Maurizio
Note: I'm the EclipseLink JAXB (MOXy) lead an a member of the JAXB 2 (JSR-222) expert group.
Below is how this can be done with MOXy's implementation of the JAXB Binder:
DataBean
package forum9988170;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="data")
public class DataBean {
private String field2;
private String field5;
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField5() {
return field5;
}
public void setField5(String field5) {
this.field5 = field5;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to add a file named jaxb.properties in the same package as your domain classes with the following entry,
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum9988170;
import java.io.File;
import javax.xml.bind.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(DataBean.class);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
File xml = new File("src/forum9988170/input.xml");
Document document = db.parse(xml);
Binder<Node> binder = jc.createBinder();
DataBean dataBean = (DataBean) binder.unmarshal(document);
dataBean.setField2("NEW FIELD 2");
dataBean.setField5("NEW FIELD 5");
binder.updateXML(dataBean);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(System.out);
t.transform(source, result);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<data>
<field1>Value</field1>
<field2>NEW FIELD 2</field2>
<field3>Value</field3>
<field4>Value</field4>
<field5>NEW FIELD 5</field5>
</data>
For More Information
http://blog.bdoughan.com/2010/09/jaxb-xml-infoset-preservation.html
http://blog.bdoughan.com/2012/01/how-does-jaxb-compare-to-xmlbeans.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
If you decide what is in structure xml then you may use XStream (http://x-stream.github.io/) to serialize and deserialize.
But if you only deserialize from xml to bean (from foreign format), then you should use Smooks (http://www.smooks.org/).
Both of these libraries are very light in contrast to JAXB. JAXB is not flexible and requires that creating XML Schema. I do not recommend, because you lose more time than on creating a simple DOM parse.
JAXB is very academic. Example: many of "SOAP envelopes" is not fully described by WSDL documents, but adds some xml into WSDL field (in a simple text field). In such a case, you lose a lot of time to create a JAXB infrastructure...
Of course this is just my personal opinion. But remember these two tools and try to use them. You'll see that it really worth.

Java to XML castor mapping

I am trying to map a POJO to an xml. Now the POJO consists of some attributes which are basic String/Integer etc and some which are other POJO classes. The xml mapping file that I am attempting to create, I want an xml element at the top level to be populated with a java attribute that is 2 levels deep.
for example, if I have 2 java class
`
class classA{
private ClassB var1
private String var2
}
class classB{
private ClassC var3;
}
class classC{
private String var4;
}
Now my xml mapping looks like
CruiseLine Mapping
<class name="ClassA"
auto-complete="false"
>
<map-to xml="Sample" />
<field name="var2">
<bind-xml node="attribute" name="var2" />
</field>
<field name="var4 from classC">
<I want a mapping for the var4 from classC to appear here. How do I do that ?>
</class>
`
As you see, I want the mapping for var4 from classC in the xml element Sample. I want it to be an element of the sample element.
`
<Sample var2="value">
<data var4="var4 value">
</Sample>
`
Help appreciated !
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
Blaise : I have not used EclipseLink before, however I think I can
move out of castor if it is relatively simple to adopt EclipseLink ?
Its just that the entire project has been using Castor so it would be
more consistent. However, could you please elaborate on Eclipselink.
Thanks. – TYS
EclipseLink MOXy is a JAXB (JSR-222) implementation. Looking at your question, this model can be mapped to the desired XML using any JAXB implementation as follows:
ClassA
package forum9994762;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Sample")
#XmlAccessorType(XmlAccessType.FIELD)
public class ClassA {
#XmlElement(name="data")
private ClassB var1;
#XmlAttribute
private String var2;
}
ClassB
package forum9994762;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class ClassB {
#XmlAttribute(name="var4")
private ClassC var3;
}
ClassC
package forum9994762;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class ClassC {
#XmlValue
private String var4;
}
Demo
package forum9994762;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ClassA.class);
File xml = new File("src/forum9994762/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
ClassA classA = (ClassA) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(classA, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Sample var2="value">
<data var4="var4 value"/>
</Sample>
Mapping File
As a Castor user, you may prefer representing your metadata as an external mapping file. EclipseLink MOXy offers such an extension:
binding.xml
<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum9994762"
xml-accessor-type="FIELD">
<java-types>
<java-type name="ClassA">
<xml-root-element name="Sample"/>
<java-attributes>
<xml-element java-attribute="var1" name="data"/>
<xml-attribute java-attribute="var2"/>
</java-attributes>
</java-type>
<java-type name="ClassB">
<java-attributes>
<xml-attribute java-attribute="var3" name="var4"/>
</java-attributes>
</java-type>
<java-type name="ClassC">
<java-attributes>
<xml-value java-attribute="var4"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Demo
package forum9994762;
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum9994762/binding.xml");
JAXBContext jc = JAXBContext.newInstance("forum9994762", ClassA.class.getClassLoader(), properties);
File xml = new File("src/forum9994762/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
ClassA classA = (ClassA) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(classA, System.out);
}
}
For More Infomation
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
If you use auto-complete=false in your mapping.xml file for ClassA <class name="ClassA" auto-complete="false" > then you must tell Castor marshaller how to map ClassB and ClassC individually.
Including these two lines may help you getting started:
<class name="ClassB" auto-complete="true">
<map-to xml="class-B" />
</class>
<class name="ClassC" auto-complete="true">
<map-to xml="class-C" />
</class>
But since var4 is a member of ClassC but not ClassB, it will be one level deep inside your xml output hierarchy.

Simple way to stub an element with JAXB?

I'm just starting to learn to use JAXB to marshal my java objects into XML. I've got no problem serializing objects, but now what I want to do is insert an element into the xml for which there is no corresponding POJO.
For example, I have a list of Folders, and I want to put each Folder into an xml element called Folders. Does this make sense? I could just write <Folders> and </Folders> to the output stream where appropriate, but I'd like to do this in a more formal way.
You could use JAXB with StAX to do this. Using a XMLStreamWriter will help formalize the interaction with the XML and give you better control over such this as encodings:
package forum8406266;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Folder.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
List<Folder> folders = new ArrayList<Folder>(3);
folders.add(new Folder());
folders.add(new Folder());
folders.add(new Folder());
XMLOutputFactory xof = XMLOutputFactory.newFactory();
XMLStreamWriter xsw = xof.createXMLStreamWriter(System.out);
xsw.writeStartDocument();
xsw.writeStartElement("Folders");
for(Folder folder : folders) {
marshaller.marshal(folder, xsw);
}
xsw.writeEndElement();
xsw.writeEndDocument();
xsw.flush();
}
}
The above code assumes that your Folder class is annotated with #XmlRootElement:
package forum8406266;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="Folder")
public class Folder {
}

Categories