how i can parse the XML using Jaxb.
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://www.xyz.in"><root><Del><NUMBER>13691991</NUMBER><PIECES>2</PIECES><SHEETNO>D1415/001005</SHEETNO></Del></root></string>
Try to create these classes:
The class Del
package jaxb;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement( name = "Del")
public class Del {
#XmlElement(name = "NUMBER")
private String number;
#XmlElement(name = "PIECES")
private String pieces;
#XmlElement(name = "SHEETNO")
private String sheetno;
public Del() {
super();
}
public void setNumber(String number) {
this.number = number;
}
public String getNumber() {
return number;
}
public void setPieces(String pieces) {
this.pieces = pieces;
}
public String getPieces() {
return pieces;
}
public void setSheetno(String sheetno) {
this.sheetno = sheetno;
}
public String getSheetno() {
return sheetno;
}
}
The class MyString
package jaxb;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement( name = "string")
public class MyString {
#XmlElement(name = "root")
private Root root;
public MyString() {
super();
}
public void setRoot(Root root) {
this.root = root;
}
public Root getRoot() {
return root;
}
}
The class Root
package jaxb;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement( name = "root")
public class Root {
#XmlElement(name = "Del")
private Del del;
public Root() {
super();
}
public void setDel(Del del) {
this.del = del;
}
public Del getDel() {
return del;
}
}
The file package-info.java
#XmlSchema( namespace="http://www.xyz.in",
xmlns={ #XmlNs( prefix = "", namespaceURI = "http://www.xyz.in")},
elementFormDefault = XmlNsForm.QUALIFIED)
package jaxb;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
**The Main class to test **
package jaxb;
import java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class Main {
public static void main(String[] args){
try {
String file = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<string xmlns=\"http://www.xyz.in\">\n" +
" <root>\n" +
" <Del>\n" +
" <NUMBER>13691991</NUMBER>\n" +
" <PIECES>2</PIECES>\n" +
" <SHEETNO>D1415/001005</SHEETNO>\n" +
" </Del>\n" +
" </root>\n" +
"</string>";
JAXBContext jaxbContext = JAXBContext.newInstance(MyString.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(file);
MyString myString = (MyString)jaxbUnmarshaller.unmarshal(reader);
System.out.println(myString);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Related
I am all new to JAXB and having wrecked my brains for a week over this I would like to ask the following question. How can I obtain default namespace declarations like these using a generic list wrapper as Blaise Doughan indroduced:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<departments xmlns="urn:example.org/departments">
<department>
<iddepartment>1</iddepartment>
<department>Deparment A</department>
<v_iddepartment>1</v_iddepartment>
</department>
<department>
<iddepartment>2</iddepartment>
<department>Department B</department>
<v_iddepartment>1</v_iddepartment>
</department>
</departments>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employees xmlns="urn:example.org/employees">
<employee>
<firstname>Tom</firstname>
<lastname>Jones</lastname>
<praefix>Dr.</praefix>
<birthdate>1970-01-01</birthdate>
<ipnumber>1234</ipnumber>
</employee>
<employee>
<firstname>Elvis</firstname>
<lastname>Knoxville</lastname>
<praefix/>
<birthdate>1970-01-02</birthdate>
<ipnumber>4567</ipnumber>
</employee>
</employees>
I have several annotated classes like these:
package org.bp24.server.table;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement(name = "department", namespace = "urn:example.org/departments")
#XmlType(propOrder = {"iddepartment", "department", "v_iddepartment"})
public class Department {
private int iddepartment;
private String department;
private int v_iddepartment;
public int getIddepartment() {
return iddepartment;
}
public void setIddepartment(int iddepartment) {
this.iddepartment = iddepartment;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public int getV_iddepartment() {
return v_iddepartment;
}
public void setV_iddepartment(int v_iddepartment) {
this.v_iddepartment = v_iddepartment;
}
}
package org.bp24.server.table;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement(name = "employee", namespace = "urn:example.org/employees")
#XmlType(propOrder = {"firstname", "lastname", "praefix", "suffix", "birthdate", "ipnumber"})
public class Employee {
private int idemployee;
private String firstname;
private String lastname;
private String praefix;
private String suffix;
private String birthdate;
private String ipnumber;
private int v_iduser;
#XmlTransient
public int getIdemployee() {
return idemployee;
}
public void setIdemployee(int idemployee) {
this.idemployee = idemployee;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getPraefix() {
return praefix;
}
public void setPraefix(String praefix) {
this.praefix = praefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public String getBirthdate() {
return birthdate;
}
public void setBirthdate(String birthdate) {
this.birthdate = birthdate;
}
public String getIpnumber() {
return ipnumber;
}
public void setIpnumber(String ipnumber) {
this.ipnumber = ipnumber;
}
#XmlTransient
public int getV_iduser() {
return v_iduser;
}
public void setV_iduser(int v_iduser) {
this.v_iduser = v_iduser;
}
}
Here is the list wrapper I use:
package org.bp24.server.xml.copy;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
public class GenericList <T> {
private List<T> list = new ArrayList<T>();
public GenericList() {
}
public GenericList(List<T> list) {
this.list = list;
}
public void add (T element){
list.add(element);
}
public boolean isEmpty(){
if(list.isEmpty()) return true;
return false;
}
public T get (int pos){
return list.get(pos);
}
#XmlAnyElement(lax=true)
public List<T> getList(){
return list;
}
}
And here is the marshalling code:
package org.bp24.server.xml.copy;
import java.util.HashMap;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import org.bp24.server.xml.GenericList;
public class MarshallMatters<T> {
private static volatile HashMap<Class, JAXBContext> contextStore = new HashMap<Class, JAXBContext>();
//synchronized to ensure orderly concurrent contextStore access
private synchronized JAXBContext getContext (Class clazz) throws JAXBException{
if(contextStore.containsKey(clazz)) return contextStore.get(clazz);
JAXBContext context = JAXBContext.newInstance(GenericList.class, clazz);
contextStore.put(clazz, context);
return context;
}
private Class getClassFromList(List<T> list){
if(!list.isEmpty()) return list.get(0).getClass();
return null;
}
public void writeXml(List<T> list) throws JAXBException{
Class clazz = getClassFromList(list);
if(clazz==null){
System.out.println("Error message");
return;
}
JAXBContext jc = getContext(clazz);
GenericList<T> genList = new GenericList<T>(list);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
QName qn = new QName(clazz.getSimpleName().toLowerCase() + "s");
JAXBElement<GenericList> jaxbe = new JAXBElement<GenericList>(qn, GenericList.class, genList);
m.marshal(jaxbe, System.out);
}
public void readXml(List<T> list) throws JAXBException{
...
}
}
Thank you very much in advance for your help.
I have two Xml files
one is
<A>
<B>xxx</B>
</A>
the other is
<A>
<B>
<C>xxx</C>
</B>
</A>
for B element, I made a ValueObject with a String field.
also, I made a XmlAdapter<Object, ValueObject> with a boolean property for B element,
and I can setAdapter(BXmlAdapter.class, new BxmlAdaper(boolean)) when unmarshalling,
so I can tell BxmlAdaper when try to convert from a String, when try to convert from a ValueObject.
if the element B has a attribute xsi:type="prefix:ValueObject" for the first xml
and xsi:type="xs:string" for the second xml. it works well. I can unmarshall all of them with one
ValueObject.
but without xsi:type, I got a instance of org.apache.xerces.dom.ElementNSImpl in XmlAdapter unmarshall method.
how can I resolve this case.
I also have two schema files for these two xml files, So I have thought maybe I can use schema file
to tell JAXB What the type of B element is. but it seems that JAXB Just uses schema file to check.
do I miss something?
Here is the samle code
Class A
package example.dto;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import example.adapter.BXmlAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class A {
#XmlJavaTypeAdapter(BXmlAdapter.class)
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
Class B
package example.dto;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="B")
public class B {
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
package-info
#javax.xml.bind.annotation.XmlSchema(elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED, namespace = "http://test/test", xmlns = #javax.xml.bind.annotation.XmlNs(prefix = "test", namespaceURI = "http://test/test"))
package example.dto;
jaxb.index
A
B
BXmlAdapter.java
package example.adapter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import example.dto.B;
public class BXmlAdapter extends XmlAdapter<Object, B> {
private final boolean flg;
public BXmlAdapter() {
this.flg = false;
}
public BXmlAdapter(boolean flg) {
this.flg = flg;
}
#Override
public Object marshal(B v) throws Exception {
if (flg) {
return v.getTest();
} else {
return v;
}
}
#Override
public B unmarshal(Object v) throws Exception {
if (flg) {
B b = new B();
b.setTest((String) v);
return b;
} else {
return (B) v;
}
}
}
A1.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<test:a xmlns:test="http://test/test">
<test:b xsi:type="test:B" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<test:test>xxxx</test:test>
</test:b>
</test:a>
A2.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<test:a xmlns:test="http://test/test">
<test:b xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">xxxx2</test:b>
</test:a>
Xml2JavaObject.java
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.xml.sax.SAXException;
import example.adapter.BXmlAdapter;
import example.dto.A;
public class Xml2JavaObject {
public static void main(String[] args) throws ClassNotFoundException,
SAXException {
try {
File file = new File("D:\\jaxb\\A1.xml");
JAXBContext jaxbContext = JAXBContext.newInstance("example.dto");
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setAdapter(BXmlAdapter.class,
new BXmlAdapter(false));
A a = (A) jaxbUnmarshaller.unmarshal(file);
System.out.println(a.getB().getTest());
} catch (JAXBException e) {
e.printStackTrace();
}
try {
File file = new File("D:\\jaxb\\A2.xml");
JAXBContext jaxbContext = JAXBContext.newInstance("example.dto");
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setAdapter(BXmlAdapter.class,
new BXmlAdapter(true));
A a = (A) jaxbUnmarshaller.unmarshal(file);
System.out.println(a.getB().getTest());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
The code above works fine,
but if there are no xsi:type in xml files.
it does not work.
the parameter of unmarshal method in BXmlAdapter is a instance of
ElementNSImpl....
And My question is
How to handle the ElementNSImpl instance
If there is no xsi:type in xml file, Can JAXB handle different xml file by using one ValueObject
the xsi:type is also in schema file. Can JAXB use schema file to decide the type? it seems JAXB
just uses schema file in validation.
Thanks
I find a little ugly way to resolve this by using #XmlMixed And XmlAdapter.
Here is the code
class A
package example.dto;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import example.adapter.BXmlAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class A {
#XmlJavaTypeAdapter(value=BXmlAdapter.class)
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
class B
package example.dto;
public class B {
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
class XmlB
package example.dto;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlMixed;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "B")
public class XmlB {
#XmlMixed
private List<Object> tempElement;
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public List<Object> getTempElement() {
return tempElement;
}
public void setTempElement(List<Object> tempElement) {
this.tempElement = tempElement;
}
}
package-info.java
#javax.xml.bind.annotation.XmlSchema(elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED, namespace = "http://test/test", xmlns = #javax.xml.bind.annotation.XmlNs(prefix = "test", namespaceURI = "http://test/test"))
package example.dto;
jaxb.index
A
XmlB
A1.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<test:a xmlns:test="http://test/test">
<test:b>
<test:test>xxxx</test:test>
</test:b>
</test:a>
A2.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<test:a xmlns:test="http://test/test">
<test:b>xxxx2</test:b>
</test:a>
BXmlAdapter.java
package example.adapter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import example.dto.B;
import example.dto.XmlB;
public class BXmlAdapter extends XmlAdapter<XmlB, B> {
private final boolean flg;
public BXmlAdapter() {
this.flg = false;
}
public BXmlAdapter(boolean flg) {
this.flg = flg;
}
#Override
public XmlB marshal(B v) throws Exception {
XmlB xmlb = new XmlB();
if (flg) {
List<Object> tempElement = new ArrayList<>();
tempElement.add(v.getTest());
xmlb.setTempElement(tempElement);
} else {
xmlb.setTest(v.getTest());
}
return xmlb;
}
#Override
public B unmarshal(XmlB v) throws Exception {
B b = new B();
if (flg) {
if (v.getTempElement() != null && v.getTempElement().size() == 1) {
b.setTest((String) v.getTempElement().get(0));
}
} else {
b.setTest(v.getTest());
}
return b;
}
}
JavaObject2Xml.java
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import example.adapter.BXmlAdapter;
import example.dto.A;
import example.dto.B;
public class JavaObject2Xml {
public static void main(String[] args) throws ClassNotFoundException {
A a1 = new A();
A a2 = new A();
B b1 = new B();
B b2 = new B();
b1.setTest("xxxx");
b2.setTest("xxxx2");
a1.setB(b1);
a2.setB(b2);
try {
File fileA1 = new File("D:\\jaxb\\A1.xml");
JAXBContext jaxbContext = JAXBContext.newInstance("example.dto");
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.setAdapter(BXmlAdapter.class, new BXmlAdapter(false));
jaxbMarshaller.marshal(a1, fileA1);
} catch (JAXBException e) {
e.printStackTrace();
}
try {
File fileA2 = new File("D:\\jaxb\\A2.xml");
JAXBContext jaxbContext = JAXBContext.newInstance("example.dto");
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.setAdapter(BXmlAdapter.class, new BXmlAdapter(true));
jaxbMarshaller.marshal(a2, fileA2);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Xml2JavaObject.java
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.xml.sax.SAXException;
import example.adapter.BXmlAdapter;
import example.dto.A;
public class Xml2JavaObject {
public static void main(String[] args) throws ClassNotFoundException,
SAXException {
try {
File file = new File("D:\\jaxb\\A1.xml");
JAXBContext jaxbContext = JAXBContext.newInstance("example.dto");
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setAdapter(BXmlAdapter.class, new BXmlAdapter(false));
A a = (A) jaxbUnmarshaller.unmarshal(file);
System.out.println(a.getB().getTest());
} catch (JAXBException e) {
e.printStackTrace();
}
try {
File file = new File("D:\\jaxb\\A2.xml");
JAXBContext jaxbContext = JAXBContext.newInstance("example.dto");
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setAdapter(BXmlAdapter.class,new BXmlAdapter(true));
A a = (A) jaxbUnmarshaller.unmarshal(file);
System.out.println(a.getB().getTest());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
The code above can help me to use one group of Classes to handle 2 or more xml file
I have an existing XML, im using Jaxb to update it. Following code fails: there are bugs raised on this issue, but couldnt get any information about the fix. Can anyone kindly help on how to resolve the issue.
package la.te.st;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Student{
String name;
int age;
int id;
public String getName(){
return name;
}
#XmlElement
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
#XmlElement
public void setAge(int age){
this.age = age;
}
public int getId(){
return id;
}
#XmlAttribute
public void setId(int id){
this.id = id;
}
#Override
public String toString() {
return this.name + " age:" + this.age + " id:" + this.id;
}
}
/**************************************/
package la.te.st;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Binder;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
public class BinderDemo {
public static void main(String[] args) {
try {
// we need a blank document to store final xml output
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbf.newDocumentBuilder();
Document document = docBuilder.parse("Student.xml");
// create JAXBContext which will be used to create a Binder
JAXBContext jc = JAXBContext.newInstance(Student.class);
Binder<Node> binder = jc.createBinder();
// set output as formatted one
binder.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// get xml node from the document
Node xmlNode = document.getDocumentElement();
// Returns the updated JAXB object
Student st = (Student)binder.updateJAXB(xmlNode);
System.out.println(st);
// set age and name
st.setAge(11);
st.setName("Sania");
System.out.println(st);
// update xml node with new data
xmlNode = binder.updateXML(st);
st.setAge(12);
st.setName("Sania");
System.out.println(st);
xmlNode = binder.updateXML(st);
// set node value to the document
document.setNodeValue(xmlNode.getNodeValue());
// finally print the edited object on stdout
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.transform(new DOMSource(document), new StreamResult(System.out));
}catch(Exception ex) {
ex.printStackTrace();
}
}
}
/*****************************/
the Student.xml file :
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<student id="10">
<age>10</age>
<name>Zara Ali</name>
</student>
The answer to my question
JaxB reference resolving
led me to further pursue the details of the issue regarding the use of XmlSeeAlso, XmlElementReference and the specification of classes involved in JaxbContext.newInstance.
I started with trying to answer the question:
Is it possible to #XmlSeeAlso on a class without no-args constructor which has #XmlJavaTypeAdapter?
i created the Junit test below. To do so I came accross:
#XmlJavaTypeAdapter w/ Inheritance
#XmlSeeAlso alternative
In this state the code is compilable and runs - it marshals o.k. but does not umarshal as expected.
The marshal result is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Message>
<BasicInfo ref="id001"/>
</Message>
Unmarshalling does not create a BasicInfo as would be expected from the code in the Adapter:
public BasicInfo unmarshal(BasicInfoRef info) throws Exception {
BasicInfo binfo=new BasicInfo();
binfo.id=info.ref;
return binfo;
}
I find this all very confusing - things seem to be somewhat contradictory and mostly do not work as I'd expect regarding the JaxB settings involved. Most combinations do not work, those that do work do not give the full result yet. I assume that is also depending on the version and implementation being used.
What nees to be done to get this working?
What is a minimal set of XmlElementRef,XmlSeeAlso and JaxbContext newInstance referencing of the necessary classes?
How can I check which JaxB Implementation is being used? I am using EclipseLink 2.3.2? and I'm not sure whether this uses MoxY or not.
Update:
after consideration of:
JAXB xsi:type subclass unmarshalling not working
http://www.java.net/node/654579
http://jaxb.java.net/faq/#jaxb_version
http://www.eclipse.org/eclipselink/documentation/2.4/moxy/type_level003.htm
Can JAXB marshal by containment at first then marshal by #XmlIDREF for subsequent references?
The modified code below is close to what is intended.
Errors that showed up while trying were:
javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: unable to marshal type "com.bitplan.storage.jaxb.TestRefId$BasicInfo$BasicInfoRef" as an element because it is missing an #XmlRootElement annotation]
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:323)
The code has quite a few comments which can be commented in and out to find out what happens if one tries ...
I still have no clue what the optimal solution would be to avoid superfluous annotations.
modified code:
package com.bitplan.storage.jaxb;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
import org.junit.Test;
/**
* Test the Reference/Id handling for Jaxb
*
* #author wf
*
*/
public class TestRefId {
#XmlDiscriminatorNode("#reforid")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlJavaTypeAdapter(RefOrId.Adapter.class)
public static class RefOrId {
#XmlAttribute(name = "reforid")
public String reforid;
public RefOrId() {
}
#XmlAttribute
public String id;
#XmlAttribute
public String ref;
public static class Adapter extends XmlAdapter<RefOrId, RefOrId> {
#Override
public RefOrId marshal(RefOrId idref) throws Exception {
if (idref == null)
return null;
System.out.println("marshalling " + idref.getClass().getSimpleName()
+ " reforid:" + idref.reforid);
if (idref instanceof Ref)
return new Id(((Ref) idref).ref);
return idref;
}
#Override
public RefOrId unmarshal(RefOrId idref) throws Exception {
System.out.println("unmarshalling " + idref.getClass().getSimpleName()
+ " reforid:" + idref.reforid);
return idref;
}
}
}
#XmlDiscriminatorValue("id")
#XmlAccessorType(XmlAccessType.FIELD)
public static class Id extends RefOrId {
public Id() {
reforid = "id";
}
public Id(String pId) {
this();
this.id = pId;
}
}
#XmlDiscriminatorValue("ref")
#XmlAccessorType(XmlAccessType.FIELD)
public static class Ref extends RefOrId {
public Ref() {
reforid = "ref";
}
public Ref(String pRef) {
this();
this.ref = pRef;
}
}
/*
* https://stackoverflow.com/questions/8292427/is-it-possible-to-xmlseealso-on-a
* -class-without-no-args-constructor-which-has
*/
#XmlRootElement(name = "BasicInfo")
public static class BasicInfo {
public BasicInfo() {
};
public BasicInfo(String pId, String pInfo) {
this.id = new Id(pId);
this.basic = pInfo;
}
// #XmlTransient
public RefOrId id;
public String basic;
}
#XmlRootElement(name = "SpecificInfoA")
// #XmlJavaTypeAdapter(BasicInfo.Adapter.class)
public static class SpecificInfoA extends BasicInfo {
public SpecificInfoA() {
};
public SpecificInfoA(String pId, String pInfo, String pInfoA) {
super(pId, pInfo);
infoA = pInfoA;
}
public String infoA;
}
#XmlRootElement(name = "Message")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({ SpecificInfoA.class, BasicInfo.class })
public static class Message {
public Message() {
};
public Message(BasicInfo pBasicInfo) {
basicInfos.add(pBasicInfo);
}
// #XmlJavaTypeAdapter(BasicInfo.Adapter.class)
// #XmlElement(name="basicInfo",type=BasicInfoRef.class)
#XmlElementWrapper(name = "infos")
#XmlElement(name = "info")
public List<BasicInfo> basicInfos = new ArrayList<BasicInfo>();
}
/**
* marshal the given message
*
* #param jaxbContext
* #param message
* #return - the xml string
* #throws JAXBException
*/
public String marshalMessage(JAXBContext jaxbContext, Message message)
throws JAXBException {
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(message, sw);
String xml = sw.toString();
return xml;
}
/**
* test marshalling and umarshalling a message
*
* #param message
* #throws JAXBException
*/
public void testMessage(Message message) throws JAXBException {
#SuppressWarnings("rawtypes")
Class[] classes = { Message.class, SpecificInfoA.class, BasicInfo.class }; // BasicInfo.class,};
// https://stackoverflow.com/questions/8318231/xmlseealso-alternative/8318490#8318490
// https://stackoverflow.com/questions/11966714/xmljavatypeadapter-not-being-detected
JAXBContext jaxbContext = JAXBContext.newInstance(classes);
String xml = marshalMessage(jaxbContext, message);
System.out.println(xml);
Unmarshaller u = jaxbContext.createUnmarshaller();
Message result = (Message) u.unmarshal(new StringReader(xml));
assertNotNull(result);
assertNotNull(message.basicInfos);
for (BasicInfo binfo : message.basicInfos) {
RefOrId id = binfo.id;
assertNotNull(id);
System.out.println("basicInfo-id " + id.getClass().getSimpleName()
+ " for reforid " + id.reforid);
// assertEquals(message.basicInfo.id, result.basicInfo.id);
}
xml = marshalMessage(jaxbContext, result);
System.out.println(xml);
}
/**
* switch Moxy
*
* #param on
* #throws IOException
*/
public void switchMoxy(boolean on) throws IOException {
File moxySwitch = new File(
"src/test/java/com/bitplan/storage/jaxb/jaxb.properties");
if (on) {
PrintWriter pw = new PrintWriter(new FileWriter(moxySwitch));
pw.println("javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory");
pw.close();
} else {
moxySwitch.delete();
}
}
#Test
public void testStackOverflow8292427() throws JAXBException, IOException {
boolean[] moxyOnList = { false };
for (boolean moxyOn : moxyOnList) {
switchMoxy(moxyOn);
System.out.println("Moxy used: " + moxyOn);
Message message = new Message(new BasicInfo("basicId001",
"basicValue for basic Info"));
message.basicInfos.add(new SpecificInfoA("specificId002",
"basicValue for specific Info", "specific info"));
message.basicInfos.add(new SpecificInfoA("specificId002",
"basicValue for specific Info", "specific info"));
testMessage(message);
}
}
}
This is the Junit Test as mentioned above (before update):
package com.bitplan.storage.jaxb;
import static org.junit.Assert.*;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import org.junit.Test;
// Test the Reference/Id handling for Jaxb
public class TestRefId {
/*
* https://stackoverflow.com/questions/8292427/is-it-possible-to-xmlseealso-on-a-class-without-no-args-constructor-which-has
*/
#XmlRootElement(name="BasicInfo")
#XmlJavaTypeAdapter(BasicInfo.Adapter.class)
public static class BasicInfo {
#XmlRootElement(name="BasicInfo")
#XmlAccessorType(XmlAccessType.FIELD)
public static class BasicInfoRef {
#XmlAttribute(name = "ref")
public String ref;
}
public static class Adapter extends XmlAdapter<BasicInfoRef,BasicInfo>{
#Override
public BasicInfoRef marshal(BasicInfo info) throws Exception {
BasicInfoRef infoRef = new BasicInfoRef();
infoRef.ref=info.id;
return infoRef;
}
#Override
public BasicInfo unmarshal(BasicInfoRef info) throws Exception {
BasicInfo binfo=new BasicInfo();
binfo.id=info.ref;
return binfo;
}
} // Adapter
public String id;
public String basic;
}
#XmlJavaTypeAdapter(BasicInfo.Adapter.class)
public static class SpecificInfoA extends BasicInfo {
public String infoA;
}
#XmlRootElement(name="Message")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({SpecificInfoA.class,BasicInfo.class})
public static class Message {
// https://stackoverflow.com/questions/3107548/xmljavatypeadapter-w-inheritance
#XmlElementRefs({
#XmlElementRef(name="basic", type=BasicInfo.class),
// #XmlElementRef(name="infoA", type=SpecificInfoA.class)
})
public BasicInfo basicInfo;
}
#Test
public void testStackOverflow8292427() throws JAXBException {
Message message=new Message();
SpecificInfoA info=new SpecificInfoA();
info.id="id001";
info.basic="basicValue";
info.infoA="infoAValue";
message.basicInfo=info;
#SuppressWarnings("rawtypes")
Class[] classes= {Message.class,SpecificInfoA.class,BasicInfo.class,BasicInfo.BasicInfoRef.class}; // BasicInfo.class,};
// https://stackoverflow.com/questions/8318231/xmlseealso-alternative/8318490#8318490
JAXBContext jaxbContext = JAXBContext.newInstance(classes);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(message, sw);
String xml=sw.toString();
System.out.println(xml);
Unmarshaller u = jaxbContext.createUnmarshaller();
Message result = (Message) u.unmarshal(new StringReader(xml));
assertNotNull(result);
assertNotNull("basicInfo should not be null",result.basicInfo);
assertEquals("id001",result.basicInfo.id);
}
}
I am using spring 3 + spring security 3 + hibernate.
I have some problems with mapping classes. I don't know why, but some classes are mapped they can be used by Hibernate but at the same time some (which are used for Spring Security) are not!
forum-security.xml:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService"/>
</authentication-manager>
</beans:beans>
UserServiceImpl:
package forum.service;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import forum.domain.ForumUser;
#Service
public class UserServiceImpl implements UserService{
#Autowired private SessionFactory sessionFactory;
#Autowired private Assembler assembler;
public List<ForumUser> listAllUsers(){
return null;
}
public List<ForumUser> listUsersBySellingPont(){
return null;
}
#Transactional
public ForumUser getUserByUsername(String username){
Session session = sessionFactory.getCurrentSession();
List<ForumUser> users = session.createQuery("from ForumUser").list();
ForumUser result = null;
for(ForumUser user : users){
if(user.getUsername().equals(username))
result = user;
}
return result;
}
public void addUser(ForumUser user){
}
public void updateUser(ForumUser user){
}
public void deleteUser(Integer id){
}
}
Assembler:
package forum.service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import forum.domain.ForumUser;
import forum.domain.UserDetailsImpl;
#Service("assembler")
public class Assembler {
#Transactional(readOnly = true)
UserDetailsImpl buildUserFromUserEntity(ForumUser userEntity) {
Integer id = userEntity.getId();
String username = userEntity.getUsername();
String password = userEntity.getPassword();
String email = userEntity.getEmail();
Date enabled = userEntity.getEnabled();
Date lastEntered = userEntity.getLastEntered();
Date registered = userEntity.getRegistered();
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (GrantedAuthority role : userEntity.getAuthorities()) {
authorities.add(role);
}
UserDetailsImpl user = new UserDetailsImpl();
user.setId(id);
user.setUsername(username);
user.setPassword(password);
user.setEmail(email);
user.setEnabled(enabled);
user.setAuthorities(authorities);
user.setLastEntered(lastEntered);
user.setRegistered(registered);
return user;
}
}
And now classes that are not mapped by hibernate (other classes are mapped):
package forum.domain;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.FetchType;
import javax.persistence.CascadeType;
import org.springframework.security.core.GrantedAuthority;
#Entity
#Table(name="users")
public class ForumUser {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column(name="username")
private String username;
#Column(name="password")
private String password;
#Column(name="email")
private String email;
#Column(name="registered")
private Date registered;
#Column(name="lastEntered")
private Date lastEntered;
#Column(name="enabled")
private Date enabled;
#OneToMany(cascade=CascadeType.REFRESH,fetch=FetchType.LAZY,mappedBy="forumUser")
private List<GrantedAuthority> authorities;
public Date getEnabled(){
return enabled;
}
public void setEnabled(Date enabled){
this.enabled = enabled;
}
public Integer getId(){
return id;
}
public void setId(Integer id){
this.id = id;
}
public String getUsername(){
return username;
}
public void setUsername(String username){
this.username = username;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password = password;
}
public String getEmail(){
return email;
}
public void setEmail(String email){
this.email = email;
}
public Date getRegistered(){
return registered;
}
public void setRegistered(Date registered){
this.registered = registered;
}
public Date getLastEntered(){
return lastEntered;
}
public void setLastEntered(Date lastEntered){
this.lastEntered = lastEntered;
}
public List<GrantedAuthority> getAuthorities() {
return authorities;
}
public void setAuthorities(List<GrantedAuthority> authorities){
this.authorities = authorities;
}
}
And the second class:
package forum.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.CascadeType;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import org.springframework.security.core.GrantedAuthority;
#Entity
#Table(name="authorities")
public class Authority implements GrantedAuthority{
#ManyToOne(cascade=CascadeType.REFRESH,fetch=FetchType.LAZY)
#JoinColumn(name="userId")
private ForumUser forumUser;
#Column(name="authority")
private String authority;
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
}
So when I am trying to retrieve an user by username from DB (UserServiceImpl.getUserByUsername() from ForumUser), it throws
org.hibernate.hql.ast.QuerySyntaxException: ForumUser is not mapped [from ForumUser]
And if I change thir HQL to another, for example "from Forum" (it is another class, that is working) it will throw another exception, doesn't really matter what exactly, but the fact is that it mappes another class and can retrieve it.
How can I solve this problem?
Oh, I have found a solution! It all was because of my own inattention. I have forgotten to add new classes to a list of annotated persistent classes in my conf.