Use custom deserializer in jackson fasterxml - 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?

Related

Using full java object package paths inline in autogenerated code

We have a pretty comic situation: a Postgres DB with a schema called Internal:
public class Internal extends SchemaImpl
Now we have to create an enum, with one of the values being called... Internal. The autogenerated code for this enum doesn't compile due to the collision between the enum name and the schema name, returned by getSchema() method:
///
import blabla.jooq.internal.Internal;
///
#Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
#SuppressWarnings({ "all", "unchecked", "rawtypes" })
public enum TypeEnum implements EnumType {
Internal("Internal"), External("External");
/////
/**
* {#inheritDoc}
*/
#Override
public Schema getSchema() {
return Internal.INTERNAL; //<<< The compiler thinks Internal is the enum value and not the schema name static object and fails
}
The two options to fix this are:
Rename the enum (which for some reason we would like to avoid)
Make the autogenerated code have the package names being included with the object references inline
Is there any configuration that will let us achieve option 2?
TIA
Bug in the code generator
That looks like a bug in jOOQ's code generator. I've created: https://github.com/jOOQ/jOOQ/issues/13692
Work around by renaming generated objects
You can work around such bugs by using a "generator strategy":
Programmatic: https://www.jooq.org/doc/latest/manual/code-generation/codegen-generatorstrategy/
Configurative: https://www.jooq.org/doc/latest/manual/code-generation/codegen-matcherstrategy/
With such a strategy, you can rename either the schema or enum class name, or both, of course, depending on your tastes:
<configuration>
<generator>
<strategy>
<matchers>
<schemas>
<schema>
<expression>INTERNAL</expression>
<schemaClass>
<transform>PASCAL</transform>
<expression>$0_SCHEMA</expression>
</schemaClass>
</schema>
</schemas>
<enums>
<enum>
<expression>INTERNAL</expression>
<enumClass>
<transform>PASCAL</transform>
<expression>$0_ENUM</expression>
</enumClass>
</enum>
</enums>
</matchers>
</strategy>
</generator>
</configuration>
Work around by avoiding imports for certain types
Alternatively, you can specify which type references should always remain fully qualified, rather than imported, see:
https://www.jooq.org/doc/latest/manual/code-generation/codegen-advanced/codegen-config-generate/codegen-generate-fully-qualified-types/
For example:
<configuration>
<generator>
<generate>
<fullyQualifiedTypes>.*\.INTERNAL</fullyQualifiedTypes>
</generate>
</generator>
</configuration>

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.

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

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.

which spring ws jaxb annotation to change xml element name

I am using a sping ws endpoint with jaxb marshalling/unmarshalling to proudce a list of Organisation objects (our local type). The endpoint is SOAP 1.1, no parameters supplied on the request message.
I understand JAXB doesn't handle lists very well, so I use a wrapper class.
#XmlRootElement(name="orgResponse", namespace=....)
public class OrganisationListWrapper {
private ArrayList<Organisation> organisationList;
public getOrganisationList() {
return organisationList;
}
public setOrganisationList(ArrayList<Organisation> organisationList) {
this.organisationList = organisationList;
}
}
The endpoint....
#PayloadRoot(localPart=.... namespace=....)
#ResponsePayload
public OrganisationListWrapper getOrganisations() {
OrganisationListWrapper wrapper = new OrganisationListWrapper();
wrapper.setOrganisationList(.... call service layer get list ....);
return wrapper;
}
This works fine and I get a SOAP payload with
<orgResponse>
<organisationList>
... contents of organisation 1
</organisationList>
<organisationList>
... comtents of organisation 2
</organisationList>
.... etc ....
</orgResponse>
The Organisation class is not JAXB annotated. It is part of a large list of pre-existing classes that are being exposed through web services for the first time. Trying to get by without going in and annotating them all by hand.
I was able to override the name OrganisationWrapper with orgResponse in the XmlRootElement annotation. I would like to override the organisationList name in the child element with organisation but haven't been able to find an annotation that does this.
I can replace the array list name with organisation and it will work fine, but our coding standard here required us to put List on the end of our list names. I would like to try and stick to that. I have tried XmlElement, but that produced a jaxb exception.
Any suggestions would be appreciated.
Because JAXB default the access type to PUBLIC_MEMBER, make sure you annotate the property (getter) and not the field:
#XmlElement(name="organisation")
public getOrganisationList() {
return organisationList;
}
If you want to annotate the field then add the following annotation to your class:
#XmlAccessorType(XmlAccessType.FIELD)

Resources