We're facing an issue when marshalling an object to xml with JAXB. In our code example (https://github.com/fbe/JaxbIssueTracker, simple maven project, test-case demonstrates what's going wrong) we have three types:
parent
hijacked
concrete
XSDs can be found here: github
The parent type is abstract, concrete extends the parent, parent references the hijacked type in a sequence. From these xsd-files the JAVA files are being generated by JAXB (xjc) without any warn- or error-messages.
When marshalling a concrete type the following xml-output is being generated by JAXB:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:concreteType xmlns:ns2="http://www.felixbecker.name/parent" xmlns="http://www.felixbecker.name/hijacked" xmlns:ns3="http://www.felixbecker.name/concrete">
<ns2:hijacked>
<value>foobar</value>
</ns2:hijacked>
</ns3:concreteType>
As you can see the hijacked-type is falsely using the ns2-prefix which refers to the namespace of the parent type - but instead it should use the default namespace which refers to the hijacked namespace.
JAXB is not able to unmarshal this generated xml although it is generated by itself with activated schema validation.
What is going wrong here? Is the xml-schema broken? Or is JAXB not able to deal with inheritance correctly?
Removing the abstract-attribute from the parent.xsd did not have any effect on the faced issue.
Any help would be highly appreciated! :-)
Used versions (both broken):
JAXB from JDK 1.6
JAXB-RI 2.2.5
Regards,
fbe & ollinux
In your example, the hijacked element is actually defined in the parent schema and so gets placed in the "parent" namespace. This means the namespaces of the marshalled document are correct. If jaxb can unmarshal the document it itself marshalled then this sounds like a bug to me.
To get the hijacked element in the correct namespace you could move the element declaration to the hijacked schema and use
<element ref="hijacked:hijacked"/>
in the parent schema.
Related
I have a central xml configuration in an Commons project. This means in this project I don´t know which xml elements in the config exists. I want to hold the root element in the configuration. The requestet configuration item from other project has to be with a class object. This class object contains neccessary information of the requestet xml Element with the jaxb annotation. Is it possible to get a filled object from JAXB with this guidelines?
I have no code examples because I don´t know yet how to start.
JAXB mapps the XML structure into Object oriented graph/hierarchy.
If you don't know the structure/hierarchy of XML, you should use push/pull parsing SAX, StAX, ...
Scenario
Consider the following class (imports omitted):
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement
public class Test {
#XmlAttribute
public int id;
#XmlAnyAttribute
public Map<QName,String> any;
}
What is the behaviour when I do the following:
Test t = new Test();
t.id = 5;
t.any = new HashMap<QName,String>();
t.any.put(new QName("id"), "10");
JAXBContext jc = JAXBContext.newInstance(Test.class);
Marshaller m = jc.createMarshaller();
m.setEventHandler(myEventHandler);
m.marshal(t, System.out);
The obvious problem:
An XML element should be generated for the instance of Test. But the obvious problem is: With which attributes? <test id="5"/>? Or <test id="10"/>? Or an invalid XML <test id="5" id="10"/>?
Please answer:
Will the marshalling be successful (or will there be an exception?)
Will handleEvent(ValidationEvent) of myEventHandler be called? If yes, with what?
What can be expected on System.out, i.e. what is the output XML? (If 1. is answered positive)
Will a (standard conforming) JAXB marshaller always generate well-formed XML? (No schema involved)
Is the behaviour of the reference implementation in the standard library, which outputs <test id="5" id="10"/> and reports absolutely no error, conforming to the JAXB specification?
Please base your answer on solid fundaments:
Of course one can just try it out. But one should be able to predict the exact behaviour with studying the Javadoc and JAXB specification documents. So, if possible, back your answer with citing the relevant part of a documentation source.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Where to Find the JAXB Specification
For reference the JAXB (JSR-222) specification can be downloaded from the following location: https://jcp.org/en/jsr/detail?id=222
Answers to Your Questions
Will the marshalling be successful (or will there be an exception?)
The JAXB 2.2 specification does not cover what will happen for this use case. Only the unmarshalling use case is defined (see B.3.6.2).
Will handleEvent(ValidationEvent) of myEventHandler be called? If yes, with what?
There is nothing in the JAXB 2.2 spec requiring this.
What can be expected on System.out, i.e. what is the output XML? (If 1. is answered positive)
The behaviour for this is undefined. As you pointed out both the reference and MOXy implementations of JAXB will output two attributes with the same name.
Will a (standard conforming) JAXB marshaller always generate well-formed XML? (No schema involved)
You have identified one case where this is not true.
Is the behaviour of the reference implementation in the standard library, which outputs and reports absolutely no error, conforming to the JAXB specification?
The marshalling behviour is undefined in the specification, therefore the behaviour of the reference (and MOXy) implementations of JAXB don't conflict with the specification.
Where to Report Issues
A seriously developed software should, IMHO, leave no doubts about
behaviour in edge cases.
You can raise issues like this either through Jira or the mailing lists.
https://java.net/jira/browse/JAXB/ (Issue Tracker)
https://java.net/projects/jaxb/lists/ (Mailing Lists)
I am trying to work out how to unmarshall and XML document to a Java document.
The top of the xml document looks like this
<xs:myData xmlns:xs="http://www.example.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com example.xsd ">
There is a schema file whose top section looks like this:
<schema targetNamespace="http://www.example.com"
elementFormDefault="qualified"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xs="http://www.example.com">
I would like to unmarshall the xml document using Spring/JaxB and eventually convert it to a JPA object. I am not sure how to go about so i looked for examples on google and came up with this http://thoughtforge.net/610/marshalling-xml-with-spring-ws-and-jaxb/
I understand most of it except how or where the schema is used.
I have seen other examples where the schema is explicitly specified, i.e.
SchemaFactory schemaFac = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema sysConfigSchema = schemaFac.newSchema(
new File("example.xsd"));
unmarshaller.setSchema(sysConfigSchema);
RootElement root = (RootElement)unmarshaller.unmarshal(
new File("example1.xml"));
How is the schema shown in the first link used to validate the xml document?
Are there any disadvantages to using Spring's jaxb2Marshaller as opposed to direct use of JAXB?
What is the effect of having the namespace next to the XmlElement annotation? (See the Person class)
I would appreciate any more examples showing Spring/REST with unmarshalling with schema validation.
Thanks
As far as I know JAXB does not parse xsi attribute to dereference XSD, load it and use for validation. Perhaps that was done to disable automatic validation, otherwise it would be problematic to switch it off :)
Spring Jaxb2Marshaller was obviously added to implement the same interface org.springframework.oxm.Marshaller (which is implemented also by CastorMarshaller, JibxMarshaller, ...). It is very powerful and allows you to tune JAXBContext in very flexible way (I can't imagine the scenario when provided API is not enough). From pattern point of new Jaxb2Marshaller is a builder, so it does not add anything to core JAXB functionality. But there are some evident advantages. For example, schema loading is very simple. In the article the Spring context refers the person.xsd (<property name="schema" value="classpath:schema/person.xsd"/>) which one need to put into resources explicitly. Then JAXB marshaller/unmarshaller will use this schema to validate XML when XML is generated/loaded.
#XmlElement(..., namepsace="xxx") will automatically generate this XML element with a specified namespace. It's rare case if somebody does not use namespaces. I would say writing XSD without namespaces is not normal, as you want to avoid the element name collision.
Using JAXB with RestTemplate is very simple. You need to be sure that JAXB runtime is in your classpath (JDK 6 already has it) and your bean is annotated with #XmlRootElement. Then just use Person person = restTemplate.getForObject(restServiceUrl, Person.class),
this is my first question, so please be gentle ;)
I'm stuck with a weird problem. Essentially i get three XSD definitions like the following:
PartA.xsd
targetNameSpace="PartA"
include="PartB.xsd"
PartB.xsd
<!-- no namespace definition!!! -->
PartC.xsd
targetNameSpace="PartC"
inlude="PartB.xsd"
import="PartA.xsd"
The error pops up, when binding PartC via JAXB to Java classes:
A class/interface with the same name "b.exampleType" is already in use. Use a class customization to resolve this conflict.
This confusing error happened most likely because the schema uses a technique called "chameleon schema", which causes a single definition to be loaded multiple times into different namespaces. See http://forums.java.net/jive/thread.jspa?threadID=18631 for more about this.
Following the link, i found out, the actual error lies in PartB, which has no namespace declaration! This method is called Chameleon Schema. The defined types in PartB will adopt the namesspace of the importing XSD.
So in my case, there are two namespaces for the same type:
"PartA"
"PartC"
And this is, where JAXB breaks down. I haven't found a way to bind PartC properly. And (to make things tricky) i have chance to change the original XSD definitions!
Has anyone come across this phenomena or something like that before and has a valid workaround for it?
I was facing the same problem using wsdl2java:
WSDLToJava Error: Thrown by JAXB : A class/interface with the same
name "Respuesta" is already in use. Use a class customization to
resolve this conflict.
But this question pointed me in the right direction.
Using wsdl2java from CFX you can customize how elements are binded to classes using a binding.xml file.
For example:
/Applications/apache-cxf-2.7.13/bin/wsdl2java -b ./src/main/resources/binding.xml -V -d src/main/java -compile -classdir target/classes http://someurl.wsdl
The key is to explain in the binding.xml to name certain xsd element with one especific className to avoid colissions:
<jxb:bindings xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" version="2.1">
<jxb:bindings schemaLocation="./someXsdFile.xsd">
<!-- Rename the respuesta class to resolve a naming conflict with other Respuesta element already defined-->
<jxb:bindings node="//xs:element[#name='respuesta']/xs:complexType">
<jxb:class name="Respuesta2" />
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
Hope this helps to the next person with this problem using wsdl2java. I supose that other tools should allow similar aproaches to this problem.
I was having the same issue and the google search landed me here.
Your question is detailed enough and I was able to find the answer,
what I did is put the namespace in PartB.xsd and use XJC to generate the java classes.
I added the following:
xmlns:ns="http://www.myCompany.com/2009/01/CustSchema" targetNamespace="http://www.myCompany.com/2009/01/CustSchema"
The following is available, although it does not provide a lot of detail:
https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#compiling-xml-schema-how-modularization-of-schema-interacts-with-xjc
There is one workaround for this problem. More specifically what you have to do is:
1.Define proxy schema (PartB_proxy.xsd) for PartB.xsd (with no namespace definition) and specify temporary namespace (for example PartB):
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" xmlns="PartB"
targetNamespace="PartB">
<xsd:include schemaLocation="partB.xsd"/>
2.Prepare bindings file (for large schemas this step could be automatized by generating episode file - more details in my article - I've attached link at the end of this post) - this step is required to avoid generating duplicate classes for PartB schema (by default the duplicate classes that come from PartB schema will be generated for PartA and PartC schemas/java packages):
<bindings version="2.1" xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<bindings scd="x-schema::partA" xmlns:partA="PartA">
<bindings scd="~partA:SomeType1">
<class ref="SomeType1"/>
</bindings>
<bindings scd="~partA:SomeType2">
<class ref="SomeType2"/>
</bindings>
</bindings>
<bindings scd="x-schema::partC" xmlns:partC="PartC">
<bindings scd="~partC:SomeType1">
<class ref="SomeType1"/>
</bindings>
<bindings scd="~partC:SomeType2">
<class ref="SomeType2"/>
</bindings>
</bindings>
</bindings>
3.Generate JAXB classes using XJC, example Gradle task (I am using Moxy implemetnation but You can use default XJC generator from JDK 8 or standalone JAXB library for newer JDK versions):
task generateExampleChameleonSourcesWithMoxy(type: JavaExec) {
classpath = configurations.moxy
main = 'org.eclipse.persistence.jaxb.xjc.MOXyXJC'
args '-b'
args 'src/main/resources/example/chameleon/bindings.xml'
args '-d'
args 'src/main/generated'
args 'src/main/resources/example/chameleon/PartA.xsd'
args 'src/main/resources/example/chameleon/PartB_proxy.xsd'
args 'src/main/resources/example/chameleon/PartC.xsd'
}
4.Remove proxy namespace (partB) from package-info.java generated for partB schema:
javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package partB;
5.Finally You can marshal and unmarshal these XML documents using the Default Namespace Remapping technique by:
defining the default namespace for the types with no namespace:
com.sun.xml.internal.bind.defaultNamespaceRemap property (JDK JAXB >=JDK 1.8)
com.sun.xml.bind.defaultNamespaceRemap property (External JAXB JDK 1.9+)
performing marshalling/unmarshalling as usual
Here’s the code that is responsible for that (JDK 1.8):
//Initialize JaxbContext
Map<String, String> properties = new HashMap<>();
properties.put("com.sun.xml.internal.bind.defaultNamespaceRemap", "partC");
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[]{SomeType1.class}, properties);
//Unmarshall Message object file from example XML
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object messageObject = unmarshaller.unmarshal(getClass().getResourceAsStream("/example/chameleon/Division.xml"));
//Marshall message object back into XML
ByteArrayOutputStream xmlMessageOS = new ByteArrayOutputStream();
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(messageObject, xmlMessageOS);
System.out.println(xmlMessageOS.toString());
//Validate output XML with schema
InputStream xmlInputStream = new ByteArrayInputStream(xmlMessageOS.toByteArray());
Source xmlSource = new StreamSource(xmlInputStream);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(getClass().getResource("/example/chameleon/PartC.xsd"));
Validator validator = schema.newValidator();
validator.validate(xmlSource);`
Unfortunately, You have to use different JAXB contexts for JAXB objects coming from PartA and PartC namespaces. This is required because JAXB default namespace remapping property is set per JAXB context - this way JAXB marshaller/unmarshaller knows how to deal with POJO objects that have no namespace defined (PartB).
Nevertheless, I do not think it is problematic as usually all the JAXB contexts are initialized once - during the application startup (in singleton).
To sum up, the workaround described above allows You to use JAXB to marshall/unmarshall XML documents without modifying the chameleon schemas. The proxy XSD file is used only at the beginning - for the process of generating JAXB objects (XJC). The marshalled/unmarshalled XML documents still conform to the original schemas. This technique is useful especially if You are not allowed to modify the schemas. I was dealing with that problem for read-only NDC standard (set of schemas) in 18.1 version defined by http://www.iata.org.
I've written an article on that topic: How to deal with Chameleon namespace design in JAXB. You will find it here: https://medium.com/#pziobron/how-to-deal-with-chameleon-namespace-design-in-jaxb-e36bcc03767d. Hopefully, You will find it useful.
I am creating amazon feed, the feed xml should be like:
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
I am using jaxb to generate xml files from java classes, I used NamespacePrefixMapperImpl from jaxb samples to add namespaces to the xml. But currently it generates the root like:
<AmazonEnvelope xmlns:xsi:noNamespaceSchemaLocation="amzn-envelope.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
how I can remove the xmlns from amzn-envelope.xsd??
Here's the mapping I used in NamespacePrefixMapperImpl
if( "http://www.w3.org/2001/XMLSchema-instance".equals(namespaceUri) )
return "xsi";
if("amzn-envelope.xsd".equals(namespaceUri))
return "xsi:noNamespaceSchemaLocation";
I found a property at Marshaller that can add the amzn-envelope.xsd:
marshaller.setProperty("jaxb.noNamespaceSchemaLocation", "amzn-envelope.xsd");
and left the NamespacePrefixMapper to add the "http://www.w3.org/2001/XMLSchema-instance".
Hope this helps others.
If I understand your intent, your document has a default namespace, and you're trying to add the schemalLocation for that namespace.
NamespacePrefixMapper won't let you do this, it's useful only for picking a prefix for a namespace. There are no namespaces in this document, and so no useful way of using NamespacePrefixMapper. It can't be used for adding schemaLocation hints - those are treated specially by JAXB, and you're just confusing it.
Are you sure you need the noNamespaceSchemaLocation="amzn-envelope.xsd" at all? Have you tried sending it to the web service without it?