How Do I Use JAXB To Marshal Inherited Classes To XML? - jaxb

I have been provided with an xsd that I compiled to Java classes using JAXB.
The generated classes create an abstract class called "Event" and several classes that extend it.
e.g. "DerivedEvent"
I am using the following to marshal it to XML.
ObjectFactory objectFactory = new ObjectFactory();
DerivedEvent derivedEvent = objectFactory.createDerivedEvent();
JAXBContext context = JAXBContextImpl.newInstance("com.my.root.namespace");
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
Class partialClass = Event.class;
QName partNamespace = new QName(Event.class.getSimpleName());
Object element = new JAXBElement(partNamespace, partialClass, derivedEvent);
// Create a stringWriter to hold the XML
StringWriter stringWriter = new StringWriter();
marshaller.marshal(element, stringWriter);
String xml = stringWriter.toString();
This then outputs the wrong root element. i.e.
<Event xsi:type="DerivedEvent" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DerivedStartPeriod xsi:nil="true"/>
<DerivedEndPeriod xsi:nil="true"/>
</Event>
instead of
<DerivedEvent>
<DerivedStartPeriod xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<DerivedEndPeriod xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</DerivedEvent>
If I set the partialClass and partNamespace to DerivedEvent it outputs the correct information.
However, I can't really do that as this marshalling is in a flow where the event could be one of 50 different derived events.
I can't really change the xsd to have substitution groups as mentioned here http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html as this change is likely to be lost when I am provided with a new version of the xsd.
I need to use the partial class and namespace as the Events are not root elements.
Can I add the events to be XmlRootElements with bindings somehow?
Or is there another way to address this?

I eventually found this page that helped me add the XmlRootElement:
https://codereview.stackexchange.com/questions/1877/jaxb-xjc-code-generation-adding-xmlrootelement-and-joda-datetime
Which I got working (dependencies/build.xml etc) by using the Annotate sample here:
http://confluence.highsource.org/display/J2B/Home

Creating a JAXBElement through generated ObjectFactory, or use proper QName.
This is bad:
QName partNamespace = new QName(Event.class.getSimpleName());
It should be like this
QName partNamespace = new QName("your namespace", "DerivedEvent");
You should find qname like this in ObjectFactory probably as constant...
ObjectFactory probably contains method createDerivedEvent with one argument of type DerivedEvent. It returns JAXBElement which can be marshaled to xml.

Related

Use custom deserializer in jackson fasterxml

I have an xml file like:
<users>
<user>
<name>User</name>
<details>
<age>15</age>
...
</details>
</user>
...
</users>
I want to map this xml to List, where User class defined as:
class User {
public String userName;
public int userAge;
}
I don't want to define mapping using annotations and so on. Just want to create an custom deserializer. I know that It is possible in Jackson for json to define custom deserializer by extending the JsonDeserializer.
Is there similar possibility for xml deserialization?

Mockito with newInstance method

I have a class-under-test that has the following code :
public void getDetails (String message){
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
StringReader strReader = new StringReader(message);
InputSource inputSrc = new InputSource(strReader);
Document doc = docBuilder.parse(inputSrc);
...
}
I want to write a JUnit for this piece of code using Mockito.
I tried various things like :
DocumentBuilderFactory docBuilderFactoryMock = Mockito.mock(DocumentBuilderFactory.class);
Mockito.when(DocumentBuilderFactory.newInstance()).thenReturn(docBuilderFactoryMock);
But I get the Exception:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
DocumentBuilderFactory$$EnhancerByMockitoWithCGLIB$$23223735 cannot be returned by toString()
toString() should return String
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception *might* occur in wrongly written multi-threaded tests.
Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
If I do the following:
DocumentBuilderFactory docBuilderFactoryMock = Mockito.spy(DocumentBuilderFactory.newInstance());
DocumentBuilder documentBuilderMock = Mockito.mock(DocumentBuilder.class);
Mockito.when(docBuilderFactory.newDocumentBuilder()).thenReturn(documentBuilderMock);
docBuilderdocBuilderMockito.when(docBuilderFactoryMock.newDocumentBuilder()).thenReturn(docBuilderFactoryMock);
and debug my code then I see that the class-under-test does not use my Mock objects anywhere but creates its own objects and throws a SAXParseException at
Document doc = docBuilder.parse(inputSrc);
Unit testing is intended to test your components/classes, not the library components/classes that are used.
Your class is parsing a String as xml content and to test that, you will find that providing a set of xml Strings with known output is the best way to test.
Simply pass a known xml String to your class under test and assert that the resulting model that is parsed, contains the data that you expect for that xml content.
I don't think that you need any mocking.

marshal JAXB generated classes without XmlRootElement with Apache camel

In order to marshal jaxb classes with Apache Camel the jaxb class needs to include a XmlRootElement annotation.
When generating jaxb classes from XSD the XmlRootElement annotation might not be generated.
This will lead to an Exception during marshalling
"No type converter available to convert from type: "
As soon as I add the #XmlRootElement manually, everything works fine, but since these Jaxb classes are generated, adding the anntotation manually is no option.
According to the Camel documentation in such a case, the JaxbDataFormat can be set to 'fragement(true)
JaxbDataFormat jaxbMarshal = new JaxbDataFormat();
jaxbMarshal.setContextPath(ObjectFactory.class.getPackage().getName());
jaxbMarshal.setFragment(true);
Unfortunately I still get the same exception.
Is there a way to configure JaxbDataFormat different, i.e. to define the JAXBElement which is the root element, like I would do in Java
marshaller.marshal( new JAXBElement( new QName("uri","local"),
MessageType.class, messageType ));
or is there another strategy available to get the XML marshalled?
EDIT
the used route :
from("file://inbox").unmarshal(jaxbDataFormat)
.marshal(jaxbDataFormat).to("file://outbox");
the stacktrace:
java.io.IOException: org.apache.camel.NoTypeConversionAvailableException: No type converter
available to convert from type: com.xyz.AddressType to the required
type: java.io.InputStream with value com.xyz.AddressType#32317e9d at
org.apache.camel.converter.jaxb.JaxbDataFormat.marshal(JaxbDataFormat.java:148)
~[camel-jaxb-2.16.0.jar:2.16.0] at
org.apache.camel.processor.MarshalProcessor.process(MarshalProcessor.java:83)
~[camel-core-2.16.0.jar:2.16.0] at
...
[na:1.8.0_25] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_25]
Caused by: org.apache.camel.NoTypeConversionAvailableException: No
type converter available to convert from type: com.xyz.AddressType to
the required type: java.io.InputStream with value
com.xyz.AddressType#32317e9d at
org.apache.camel.impl.converter.BaseTypeConverterRegistry.mandatoryConvertTo(BaseTypeConverterRegistry.java:185)
~[camel-core-2.16.0.jar:2.16.0] at
...
In Camel 2.17, the #XmlRootElement was not required. As of 2.21, it is. Unless...
The class org.apache.camel.converter.jaxb.FallBackTypeConverter changed it's implementation from:
protected <T> boolean isJaxbType(Class<T> type) {
return hasXmlRootElement(type) || JaxbHelper.getJaxbElementFactoryMethod(camelContext, type) != null;
}
To:
protected <T> boolean isJaxbType(Class<T> type) {
if (isObjectFactory()) {
return hasXmlRootElement(type) || JaxbHelper.getJaxbElementFactoryMethod(camelContext, type) != null;
} else {
return hasXmlRootElement(type);
}
}
By default the isObjectFactory() method returns false. If you set the property CamelJaxbObjectFactoryon your CamelContext to true. then the JaxbHelper.getJaxbElementFactoryMethod(camelContext, type) will return true and the deserialization works again as before without the need for an #XmlRootElement. For completeness:
<camelContext xmlns="http://camel.apache.org/schema/spring" id="camelContext">
<properties>
<property key="CamelJaxbObjectFactory" value="true"/>
</properties>
</camelContext>
I experienced the equivalent behaviour with JaxB (#XmlRootElement annotation not present in the generated class), and I suppose it comes from the way the root element is defined in the XML schema.
For example:
<xsd:element name="DiffReport" type="DiffReportType" />
<xsd:complexType name="DiffReportType">
...
</xsd:complexType>
it will generate you the DiffReportType class without the #XmlRootElement annotation. But if you directly define your root element as following, you'll get the annotation set in your generated class (the name of the root class is then DiffReport in my example).
<xsd:element name="DiffReport">
<xsd:complexType>
...
Note: I used the first way to define the complex types in my schema for class name consistency.
You can use the "partClass" option of the jaxb data format of camel. Your question is answered in the camel docs for jaxb, which describes how to marshall XML fragments (or XML generated without the XmlRootElement annotation).
Use partClass and provide the actual class name to which you wish to marshall. In case of marshalling you also have to provide the partNamespace which is the target namespace of the desired XML object.

JAXB MOXy Binder issue when using #XmlAnyElement annotation

I have an issue when using the Binder implementation in MOXy.
Here is the input XML document (input.xml)
<?xml version="1.0" encoding="utf-8"?>
<root>
<unmapped />
</root>
And now, here is the source code used to unmarshal XML into a Binder instance and then update the XML from the corresponding Java object:
JAXBContext context = JAXBContext.newInstance(Input.class);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
Document document = builder.parse(new File("input.xml"));
Binder<Node> binder = context.createBinder(Node.class);
Input input = (Input) binder.unmarshal(document);
binder.updateXML(input);
In the end, the very simple Input class file:
#XmlRootElement(name = "root")
public class Input {
#XmlAnyElement
protected Object[] elements;
}
When the updateXML() method is invoked, the following exception is thrown:
java.lang.NullPointerException
at org.eclipse.persistence.internal.jaxb.DomHandlerConverter.convertObjectValueToDataValue(DomHandlerConverter.java:97)
We have been able to confirm this issue and it looks like it will be a very quick fix. You can use the link below to track our progress on this issue.
http://bugs.eclipse.org/391237
UPDATE
A fix has been checked into the EclipseLink 2.5.0 stream, a nightly download can be obtained from the following location:
http://www.eclipse.org/eclipselink/downloads/nightly.php
We have also checked in a fix to the EclipseLink 2.4.2 stream. A nightly download can be obtained from the above location starting October 12, 2012.

DSL Add Root Element to Serialization

I am looking for help to achieve the following
The Diagram represents a car, users can add engine and colour
when I view the XML it looks like this:
<Car>
<Engine>BigEngine</Engine>
<Colour>Pink</Colour>
</Car>
What I would like to do is to wrap the car inside 'vehicle', i.e
<Vehicle>
<Car>
<Engine>BigEngine</Engine>
<Colour>Pink</Colour>
</Car>
</Vehicle>
I am not sure of the best way to achieve this. I want the model explorer and the generated XML to be wrapped in 'vehicle' but for all other intents and purposes the user is working with a car only
Info: Visual Studio 2010, C# and DSL SDK for 2010
I would try two different approaches:
1st: override DSL Package class DocData
In DocData.cs file and override method
protected override void OnDocumentSaved(System.EventArgs e)
and then I would create the wrapper
afterwards I'd override in DocData.cs
protected override void OnDocumentLoading(System.EventArgs e)
and before calling the base method base.OnDocumentLoading(e); i would delete from the file.
2nd: Under DSL Explorer go to XML Serialization Behaviour and set Car Domain Class "Is Custom = true".
This solution is not straightforward but it's not as complicated as it seems at the first place. You'll must define every single method but for each custom method you can call a DSL generated method called "DefaulMethod" which has the default DSL serializer behaviour.
I am currently using VS 2005, so some things might have changed...
I have fixed this by the following. I am double deriving the Car class and in the Car serializer I am doing this:
Writing the extra elements:
public partial class CarSerializer : CarSerializerBase
{
public override void Write(SerializationContext serializationContext, ModelElement element, XmlWriter writer, RootElementSettings rootElementSettings)
{
// Adds the Model and LobSystem root elements to match that required by the SharePoint BCS
writer.WriteStartElement("Garage");
writer.WriteStartElement("Cars");
base.Write(serializationContext, element, writer, rootElementSettings);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
To be able to read this back in I am overriding the Car LoadModel method in the SerializationHelper and where it is getting the reader I am reading the elements until I get to Car.
....
XmlReader reader = XmlReader.Create(fileStream, settings);
reader.MoveToContent();
while (!reader.EOF && !reader.Name.Equals("Car"))
{
reader.Read();
}
reader = reader.ReadSubtree();
// using (global::System.Xml.XmlReader reader = global::System.Xml.XmlReader.Create(fileStream, settings))
using (reader)
{
....

Resources