Matching namespaces on marshalling / unmarshalling in JAXB
I use JAXB to marshall / unmarshall XML. If I marshall an XML file like this:
<om:RequestCreateEvent xmlns:om="http://ossj.org/xml/OrderManagement/v1-0" xmlns:v1="http://ossj.org/xml/Common/v1-5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:v11="http://ossj.org/xml/Common-CBECore/v1-5" xmlns:v12="http://ossj.org/xml/Common-CBEBi/v1-5">
<om:event>
<v1:applicationDN>System/JSR264/ApplicationType/OrderManagement/Application/1-0;1-0-2;ReferenceImplementation/</v1:applicationDN>
<v1:eventTime>2008-11-12T07:39:34.896+01:00</v1:eventTime>
<om:requestValue xmlns:v1="http://ossj.org/xml/om/ri/omimpl/v1-0" xsi:type="v1:ProductOrderImplValue">
<v13:key xmlns:v1="http://ossj.org/xml/OrderManagement/v1-0" xmlns:v13="http://ossj.org/xml/Common/v1-5" xsi:type="v1:ProductOrderKey">
<v13:type>http://ossj.org/xml/om/ri/omimpl/v1-0#ProductOrderImplValue</v13:type>
<v13:primaryKey>12</v13:primaryKey>
</v13:key>
<v1:requestState>open.not_running.not_started</v1:requestState>
<v12:description xsi:nil="true"/>
</om:requestValue>
</om:event>
</om:RequestCreateEvent>
and then subsequently try to unmarshall it, I get this:
<ns4:RequestCreateEvent xmlns="http://ossj.org/xml/Common/v1-5" xmlns:ns2="http://ossj.org/xml/Common-CBECore/v1-5" xmlns:ns3="http://ossj.org/xml/Common-CBEBi/v1-5" xmlns:ns4="http://ossj.org/xml/OrderManagement/v1-0" xmlns:ns5="http://ossj.org/xml/om/ri/omimpl/v1-0" xmlns:ns6="http://ossj.org/xml/Common-CBEDatatypes/v1-5" xmlns:ns7="http://ossj.org/xml/Common-CBELocation/v1-5" xmlns:ns8="http://ossj.org/xml/Common-CBEResource/v1-5" xmlns:ns9="http://ossj.org/xml/Common-CBEService/v1-5" xmlns:ns10="http://ossj.org/xml/Common-CBEProduct/v1-5" xmlns:ns11="http://ossj.org/xml/Common-CBEProductOffering/v1-5" xmlns:ns12="http://ossj.org/xml/Common-CBEParty/v1-5">
<ns4:event>
<applicationDN>System/JSR264/ApplicationType/OrderManagement/Application/1-0;1-0-2;ReferenceImplementation/</applicationDN>
<eventTime>
2008-11-12T07:39:34.896+01:00</eventTime>
<ns4:requestValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns5:ProductOrderImplValue">
<key xsi:type="ns4:ProductOrderKey">
<type>
http://ossj.org/xml/om/ri/omimpl/v1-0#ProductOrderImplValue</type>
<primaryKey/>
</key>
<ns5:requestState>open.not_running.not_started</ns5:requestState>
<ns3:description xsi:nil="true"/>
</ns4:requestValue>
</ns4:event>
</ns4:RequestCreateEvent>
I need to ensure that the prefixes used in the namespaces when I marshall the jaxb generated pojo to create the XML file match those used when
I unmarshall the same file.
Possibly there is a solution in using a NamespaceContext when I do the marshalling. However I cannot hard-code the prefixes and their uris in an implementation
of NamespaceContext because I do not have this information available to me(I use lots of schemas etc). So if I were to try and use a NamespaceContext I would need to be able to
get the prefixes and their uris from the JAXB unmarshaller which I cant seem to get.
So if anyone has any suggestions on a NamespaceContext solution or indeed some other approach I would appreciate it.
After a quick look at your XML documents that appear to be namespace qualified the same but use different prefixes. Different JAXB implementations provide different mechanisms for controlling the prefixes used, but will involve interaction on your part:
JAXB RI/Metro JAXB
The JAXB RI/Metro offer an extension called the NamespacePrefixMapper:
http://jaxb.java.net/guide/Changing_prefixes.html
EclipseLink JAXB (MOXy)
MOXy (I'm the tech lead) will use the prefixes specified in the #XmlSchema annotation. In the example below the atom prefix will be used for the http://www.w3.org/2005/Atom namespace URI.
javax.xml.bind.annotation.XmlSchema(
xmlns = {
#javax.xml.bind.annotation.XmlNs(prefix = "atom", namespaceURI = "http://www.w3.org/2005/Atom")
})
package org.example.domain;
How to customize namespace prefixes on Jersey(JAX-WS) (related question)
Related
Is it a good practice to add redundant #JsonProperty when using JacksonMapper? Even if the class vars are same named as the incoming json(and you deserialize the json)?
What can be possible upsides?
What can be possible downsides?
The downside,as you suggested, it's reduandant,especially when this class was replaced from codehaus package to fasterxml so if you have old property you will have to refactor class
org.codehaus.jackson is an older version of Jackson.
com.fasterxml.jackson represents the new project and package.
I am trying to replace the standard Jackson as it is described here: http://camel.apache.org/json.html
I am using the following configuration
<bean name="myJsonObjectMapper" class="com.my.app.MyJsonObjectMapper" primary="true"/>
<camel:camelContext id="camel-client">
<camel:template id="camelTemplate"/>
<camel:dataFormats>
<camel:json id="json" library="Jackson" objectMapper="myJsonObjectMapper"/>
</camel:dataFormats>
</camel:camelContext>
MyJsonObjectMapper extends ObjectMapper. I can see it in Spring context, I can autowire and use it. I see that Camel context is started OK, I am using the Camel version 2.20 where this feature is enabled and fixed. However when I am trying to parse JSON I am getting the exceptions which will non-modified ObjectMapper generate.
When debugging I see that JacksonDataFormat is not initialized with customized ObjectMapper but rather creates a new one on doStart() method.
What am I missing on the Camel configuration?
UPD:
I am using that mapper in scope of Camel REST DSL routes in one of the following ways:
restConfiguration().component("servlet").bindingMode(RestBindingMode.json);
rest("/somepath")
.description("blah")
.post("/subpath/")
.type(MyRQ.class)
.outType(MyRS.class)
.route().id("under-test")
.bean(service)
.endRest();
or
restConfiguration().component("servlet");
rest("/somepath")
.description("blah")
.post("/subpath/")
.route().id("under-test")
.unmarshal().json(JsonLibrary.Jackson)
.bean(service)
.marshal().json(JsonLibrary.Jackson)
.endRest();
In both cases I am getting the marshaller error due to the usage of the default marshaller instead of custom one. The routes seem to be started in scpe of the correct context, the same I am configuring ObjectMapper for.
I have a XSD file, from which I want to generate C# and Java classes as well.
I first set the namespace in the XSD according to my C# namespace where my classes resides. The generation (with the Microsoft tools) works fine and also the serialisation works great and I can validat them against the XSD - perfect.
Now I want to create java classes with JAXB.
The problem is that the classes which are going to be created have a different package structure then the one in C#. So when I set the XSD namespace to the package structure of java, it works fine. I can serialize and validate the XML.
Now my question(s):
Is there a way to solve this? (Have one XSD for both generation tools)
Do I lack a understanding of what the namespace actually is needed for?
Thank you
Edit: Since it seems to be that there is a missunderstanding, I added an example
XSD: targetNamespace = "http://foo.bar/mySubNs/model"
C# Modelnamespace: com.foo.mySubNs.model (which fits the XSD namespace)
all generated classes will have the same namespace provided though the MS codegen
Java Modelnamespace : com.foo.myOtherSubNs.model (which differs from the XSD namespace)
the generated classes will have the "C# namespace". As a result the classes will not compile.
If I would change the namespace during the code generation for java, I can compile the classes. So far so good. But I won't be able to validate the generated XML by that java classes against the XSD, since the namespace differs.
To marshall my objects in Java, I use JAXB like this:
ValidationEventCollector validationCollector = new ValidationEventCollector();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(new File ("my/schema/location"));
// JAXB_CONTEXT is just an instance of "JAXBContext"
Marshaller marshaller = JAXB_CONTEXT.createMarshaller();
marshaller.setSchema(schema);
marshaller.setEventHandler(validationCollector);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
JAXBElement<MyClass> root = new JAXBElement<MyClass> ( new QName(MyClass.class.getPackage().getName(),"MyClass"),MyClass.class, node);
marshaller.marshal(root, new File("output/Path/obj.xml"));
To build my java classes from the schema I use a xjc-task in an ant build script:
<xjc destdir="${dir.src.gen}" removeOldOutput="no" extension="true">
<schema dir="${dir.schema}" includes="${file.schema}"/>
<binding dir="${dir.schema}" includes="*.xjb"/>
<produces dir="${dir.src.gen}" includes="**/*.java"/>
</xjc>
The XSD namespace doesn't have to match the package structure, at least not in Java. When generating the classes using JAXB just provide the package you want to put the classes into.
You must take your pick as to what model is the main one: XSD, C#, or Java code. Once you make that choice, you must let the other two vary as they may. The best choice would be to make your XSD the reference model, generate the code in both languages with their respective tools, and just accept the results.
You can also try to pick the XML namespace such that the code at both ends will be satisfactory, but don't try to force anything to the last letter. That's not how it's meant to work.
I'm trying to use SerializationConfig.Feature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS but I'm not configuring the mapper myself, relying on annotations exclusively and letting Spring's RestTemplate (de)serialize automatically. Is there a way to enable the aforementioned feature in this scenario (i.e. annotations only)?
NOTE: I'm using Jackson 1.x and can't upgrade due to other libs...
With JAX-RS (like DropWizard) you can actually annotated resource endpoints, using #JacksonFeatures
public class Resource {
#Path("item")
#GET
#JacksonFeatures(serializationEnable={ SerializationFeature.WRAP_ROOT_VALUE })
public Pojo getItem(String id) {
...
}
}
I don't know if Spring exposes similar functionality, but it seems possible it does. And if not, it is something they should be able to add to allow per-endpoint setting/clearing of SerializationFeatures / DeserializationFeatures. So if it is not available, maybe file a feature request for Spring project?
Yes, it is possible.
checkout this link: http://jackson.codehaus.org/1.7.0/javadoc/org/codehaus/jackson/map/annotate/JsonSerialize.html
Example:
#JsonSerialize(using=MySerializer.class,
as=MySubClass.class,
include=JsonSerialize.Inclusion.NON_NULL,
typing=JsonSerialize.Typing.STATIC
)
Is there any way to instruct Jackson 2 ObjectMapper to use custom Classloader to deserialize JSON strings?
Here is the solution:
ClassLoader myClassLoader = ...
ObjectMapper om = new ObjectMapper();
TypeFactory tf = TypeFactory.defaultInstance()
.withClassLoader(myClassLoader);
om.setTypeFactory(tf);
Take a look at the Jackson Wiki for Modules, a Jackson Module can be used to create custom serialization / deserialization. While the wiki talks about what they can do
Add custom serializers and deserializers
Add mix-in annotations
Override or add AnnotationIntrospector
Access SerializationConfig and DeserializationConfig settings.
It is pretty light on details, just giving a very basic example. The Module JavaDoc is light on details as well. Fortunately the wiki also has a list of existing Jackson Modules on GitHub, which is more helpful as examples if you need to create your own Module from scratch.
To preserve the existing TypeMapper, you might want to do something like this (this is Scala, but same pattern works in Java), where you adjust the current type factory.
def updateClassLoader(cl: ClassLoader): Unit = {
objectMapper.setTypeFactory(objectMapper.getTypeFactory.withClassLoader(cl))
}
This will preserve cached type data from possible prior configuration calls like this:
mapper.registerModule(new Jdk8Module)
.registerModule(new JavaTimeModule)
.registerModule(new DefaultScalaModule)