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

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>

Related

MOXy JAXB breaks on XmlIDREF to type that has an enum attribute

I have set up a minimal working example of the problem I have. These are the JAXB classes.
moxytest/A.java
package moxytest;
#XmlRootElement
public class A {
#XmlElement(name = "b")
public List<B> bs;
#XmlElement(name = "c")
public List<C> cs;
}
moxytest/B.java
package moxytest;
public class B {
#XmlAttribute
#XmlID
public String id;
#XmlAttribute
public EnumD md;
}
moxytest/C.java
package moxytest;
public class C {
#XmlAttribute
#XmlIDREF
public B b;
}
moxytest/EnumD.java
package moxytest;
#XmlEnum
public enum EnumD {
VALUE1, VALUE2, VALUE3
}
Example input:
<?xml version="1.0" encoding="UTF-8" ?>
<a>
<b id="b1" md="VALUE1"/>
<b id="b2" md="VALUE2"/>
<b id="b3" md="VALUE3"/>
<c b="b2"/>
<c b="b1"/>
<c b="b3"/>
</a>
So C elements are referencing B elements by id, and B elements have an Enum attribute.
This line of Java code
JAXBContext context = JAXBContext.newInstance(A.class);
Produces an exception with the following message:
The #XmlAttribute property b in type moxytest.C must reference a type that maps to text in XML. moxytest.B cannot be mapped to a text value.
I have been debugging and reading some lines of MOXy source code. That is how I was able to set up this minimal example. The JDK implementation works fine.
EDIT:
I am using EclipseLink 2.6.0 (thanks Santhosh Kumar Tekuri)
I tested your code with following maven dependency:
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.5.0</version>
</dependency>
i placed jaxb.properties in same package where model classes exist. this file contains:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
and it works fine. below is my unmarshalling code:
public static void main(String[] args) throws Exception{
JAXBContext context = JAXBContext.newInstance(A.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(new File("input.xml"));
System.out.println(obj);
}
make sure you are using same moxy version I am using.

Force JAXB to ignore getters/setters on 3rd-party class file

Background
I have a class in a JAR file called Attachment. The important parts of the definition are below.
public class Attachment
{
public List<Media> media;
public List<Media> getMedia()
{
return this.media;
}
public void setMedia(List<Media> media)
{
this.media = media;
}
}
I am attempting to use JAXB-impl 2.1.3 to deserialize this with the following code:
JAXBContext jaxbContext = JAXBContext.newInstance(Attachment.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(text);
Attachment attachment = (Attachment) unmarshaller.unmarshal(reader);
However, this gives me the following error (snipped for brevity)
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException:
1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "media"
this problem is related to the following location:
at public java.util.List com.thirdparty.Attachment.getMedia()
...
this problem is related to the following location:
at public java.util.List com.thirdparty.Attachment.media
...
I understand that the issue is that, by default, JAXB will use an access type of PUBLIC_MEMBER, which is defined as:
Every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.
Question
So, my question is, how can I tell JAXB to ignore the field and just use the getter/setter. Note that I would prefer it this way round, as there are a number of private fields that I would need to ignore (I believe that Attachment has been set public in error in fact).
Take a look at Annox.It looks to have what you need.
JAXB reference implementation can be configured with a special annotation reader which may implement a different strategy for reading annotations. Annox takes advantage of this feature and implements an annotation reader which can load JAXB annotations from XML.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
MOXy offers an external mapping document extension to support 3rd party classes.
Sample Mapping Document
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="blog.bindingfile">
<xml-schema
namespace="http://www.example.com/customer"
element-form-default="QUALIFIED"/>
<java-types>
<java-type name="Customer">
<xml-root-element/>
<xml-type prop-order="firstName lastName address phoneNumbers"/>
<java-attributes>
<xml-element java-attribute="firstName" name="first-name"/>
<xml-element java-attribute="lastName" name="last-name"/>
<xml-element java-attribute="phoneNumbers" name="phone-number"/>
</java-attributes>
</java-type>
<java-type name="PhoneNumber">
<java-attributes>
<xml-attribute java-attribute="type"/>
<xml-value java-attribute="number"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Bootstrapping from External Mapping Document
Below is an example of how you create the JAXBContext:
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, "blog/bindingfile/binding.xml");
JAXBContext jc = JAXBContext.newInstance("blog.bindingfile", Customer.class.getClassLoader() , properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Customer customer = (Customer) unmarshaller.unmarshal(new File("src/blog/bindingfile/input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

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>

Wrapping xml output in XStream with another element

I have this class:
Class B {
private String D;
private String E;
}
Using XStream, I would like to generate XML like this, where elements A and B are generated in the XML, even though they don't exist in the java.:
<A>
<B>
<C>
<D/>
<E/>
</C>
</B>
</A>
Possible?
You can implement and register a custom converter in the XStream instance.
For example:
XStream xstream = new new XStream(...);
xstream.registerConverter(new BConverter());
xstream.toXML(new B(),new BufferedWriter(...));
Example of the converter implementation:
class BConverter implements com.thoughtworks.xstream.converters.Converter{
#Override
public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext mc) {
B target=(B)o;
writer.startNode("A");
writer.startNode("B");
writer.startNode("C");
writer.startNode("D");
writer.setValue(target.getD());
writer.endNode();//end node D
writer.startNode("E");
writer.setValue(target.getE());
writer.endNode();//end node E
writer.endNode();//end node C
writer.endNode();//end node B
writer.endNode();//end node A
}
#Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext uc) {
//unmarshalizing logic here
}
#Override
public boolean canConvert(Class type) {
return type.equals(B.class);
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Since you are looking for an annotation based solution, you may be interested in the #XmlPath extension in MOXy.
B
The #XmlPath annotation allows you to specify your mapping as an XPath.
package forum11334385;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="A")
#XmlAccessorType(XmlAccessType.FIELD)
class B {
#XmlPath("B/C/D/text()")
private String D;
#XmlPath("B/C/E/text()")
private String E;
}
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 forum11334385;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(B.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11334385/input.xml");
B b = (B) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(b, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<A>
<B>
<C>
<D>Foo</D>
<E>Bar</E>
</C>
</B>
</A>
For More Information
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2011/08/binding-to-json-xml-geocode-example.html
http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html

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.

Categories