JAXB ignores extended element while parsing - java

I am working on a piece of code that repurposes orm xmls by adding extra elements to the standard ones. I would like to use JAXB to parse the extended configuration files and JAXB parses my example xmls fine but skips the added element.
Here is some more detail. The extending xsd looks like this:
<xsd:schema targetNamespace="http://somewhere/xml/ns/orm_extension"
xmlns="http://somewhere/xml/ns/orm_extension"
xmlns:orm="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import schemaLocation="orm_2_1.xsd" namespace="http://xmlns.jcp.org/xml/ns/persistence/orm" />
<xsd:element name="entity">
<xsd:complexType>
<xsd:complexContent xml:base="orm:entity">
<xsd:extension base="orm:entity">
<xsd:sequence>
<xsd:element name="some-context" type="someContext" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="someContext">
........
</xsd:complexType>
</xsd:schema>
and the above is also the xsd I used for generation of Java classes. As for my test xml this is the content:
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" version="2.1"
xmlns:ext="http://somewhere/xml/ns/orm_extension"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://somewhere/xml/ns/orm_extension http://somewhere/xml/ns/orm_extension">
<ext:entity>
</ext:entity>
</entity-mappings>
and the code that is use for testing is:
#Test
public void testRead() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(EntityMappings.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
EntityMappings em = (EntityMappings) unmarshaller.unmarshal(
ClassLoader.getSystemResourceAsStream("schema/test.xml"));
assertNotNull(em);
assertEquals(1, em.getEntities().size());
}
The second assert in the above test fails, meaning ext:entity tag is totally ignored. What am I missing here?

The root element in your XML is entity-mappings which is absent in your XSD.

Related

cvc-elt.1.a: Cannot find the declaration of element 'core:configuration'

Abstract
In the project I am currently working on, I have multiple modules, each defining an XSD file. When loading a configuration I plan to locate these XSD files programmatically and then validate the configuration using SchemaFactory.newSchema(Source[]), for example
public void validate(Document document, InputStream... schemas) throws Exception {
Source[] sources = new Source[schemas.length];
for (int i = 0; i < schemas.length; i++) {
sources[i] = new StreamSource(schemas[i]);
}
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(sources);
schema.newValidator().validate(new DOMSource(document.getDocumentElement()));
}
Up until now I followed this Stack Overflow post, but in order to use the aforementioned method, I think I need to build the references using different namespaces.
The resulting schema contains components from the specified sources. The same result would be achieved if all these sources were imported, using appropriate values for schemaLocation and namespace, into a single schema document with a different targetNamespace and no components of its own, if the import elements were given in the same order as the sources.
Test
With core.xsd I define the static structure of the configuration.
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:core="http://web.com/core"
targetNamespace="http://web.com/core">
<xsd:element name="configuration" type="core:configuration" />
<xsd:complexType name="configuration">
<xsd:sequence>
<!-- other elements -->
<xsd:element maxOccurs="unbounded" name="agent" type="core:agent" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="agent">
<xsd:sequence>
<!-- other elements -->
<xsd:element ref="core:agentImpl" />
</xsd:sequence>
<!-- attributes -->
</xsd:complexType>
<xsd:element name="agentImpl" type="core:agentImpl" />
<xsd:complexType name="agentImpl" abstract="true" />
</xsd:schema>
With ext.xsd I add implementations for the child element under agent. In this example, I add two agents using one XSD file, but later on, such agent definitions should be separated into several ext-*.xsd files.
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:core="http://web.com/core"
xmlns:ext="http://web.com/ext"
targetNamespace="http://web.com/ext">
<xsd:import namespace="http://web.com/core" schemaLocation="core.xsd" />
<xsd:element name="file-mover" type="ext:file-mover" substitutionGroup="core:agentImpl" />
<xsd:complexType name="file-mover">
<xsd:complexContent>
<xsd:extension base="core:agentImpl">
<!-- implementation-specific stuff -->
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="junction" type="ext:junction" substitutionGroup="core:agentImpl" />
<xsd:complexType name="junction">
<xsd:complexContent>
<xsd:extension base="core:agentImpl">
<!-- implementation-specific stuff -->
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>
However, when testing this setup with
#Test
void singleExtension() throws Exception {
try (InputStream docIn = getClass().getResourceAsStream("configuration.xml");
InputStream coreIn = getClass().getResourceAsStream("core.xsd");
InputStream extIn = getClass().getResourceAsStream("ext.xsd")) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new StreamSource(docIn));
validate(document, coreIn, extIn);
}
}
and configuration.xml
<?xml version="1.0" encoding="UTF-8"?>
<core:configuration xmlns:core="http://web.com/core" xmlns:ext="http://web.com/ext">
<agent>
<ext:file-mover />
</agent>
<agent>
<ext:junction />
</agent>
</core:configuration>
I get
org.xml.sax.SAXParseException; cvc-elt.1.a: Cannot find the declaration of element 'core:configuration'.
at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204)
at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:135)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:396)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:284)
at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.handleStartElement(XMLSchemaValidator.java:2132)
at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.startElement(XMLSchemaValidator.java:829)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.DOMValidatorHelper.beginNode(DOMValidatorHelper.java:276)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.DOMValidatorHelper.validate(DOMValidatorHelper.java:243)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.DOMValidatorHelper.validate(DOMValidatorHelper.java:189)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.ValidatorImpl.validate(ValidatorImpl.java:108)
at java.xml/javax.xml.validation.Validator.validate(Validator.java:124)
at com.spell.moreen.core.xsd.so.StackOverflowTest.validate(StackOverflowTest.java:27)
at com.spell.moreen.core.xsd.so.StackOverflowTest.singleExtension(StackOverflowTest.java:38)
... (only jupiter stuff from here on)
Conclusion
The posts I found on Stack Overflow regarding this specific exception all resolved to some namespace declaration being wrong plus one instance where the root element was not declared but only its type.
If I add xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" and xsi:schemaLocation="http://web.com/core core.xsd http://web.com/ext ext.xsd" to configuration.xml, eclipse can validate it just fine, so I assume the namespaces to be correct.
The root element is present with <xsd:element name="configuration" type="core:configuration" /> in core.xsd, which is always bound via xmlns:core="...", so I can't see anything wrong there, too.
What am I overlooking or understanding wrong?
In order to validate an XML using namespaces, that XML must be parsed with namespaces in mind. Here, this means that DocumentBuilderFactory must be set to be aware of namespaces.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true); // default is 'false'
Otherwise I guess the parser simply ignores this information, which in turn makes them unavailable for the schema validator.

marshall with xjc created nested classes

<ProductInformation Context="GL">
<Assets>
<Asset ID="assetID" UserTypeID="ID">
<Name>name</Name>
<Reference ClassificationID="id"/>
<Values>
<Value AttributeID="ID">Value1</Value>
<Value AttributeID="ID">Value2</Value>
<MultiValue AttributeID="attributeID">
<Value>value3a</Value>
<Value>value3b</Value>
</MultiValue>
</Values>
</Asset>
</Assets>
<Products>....</Products>
</ProductInformation>
I used this xml->xsd and xjc to create classes from it.
Now I want to create my ProductInformation object,and marshall it.
My problem is xjc create 3 classes and a objectfactory, and some nested classes inside ProductInformation. When I look at the avaliable methods I mostly see getters instead of setters.
"Asset" class has no such methods like;
asset.setValues(List<Value> values)
Also I ended up writing funny code like this;
ProductInformation.Assets.Asset.Values.MultiValue multivalue=new ProductInformation.Assets.Asset.Values.MultiValue();
Is this normal with Jaxb?
The answer given by Ian Roberts is the correct one. I am giving this one to provide some additional information for those people interested in not having inner classes.
XML Schema (schema.xsd)
If JAXB classes are generated from the following XML schema, both the resulting Customer and Employee classes will contain a static nested class called Address (because each contains their own definition of an address). This is in fact why static nested classes are used to avoid name conflict problems.
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
targetNamespace="http://www.example.org/company"
xmlns="http://www.example.org/company"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xsd:element name="customer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="street" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="employee">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="road" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
JAXB Binding File (binding.xml)
A bindings file is used to customize the schema to Java generation. You can specify that everything should be a top level class with localScoping="top-level". When you do this you must make sure to resolve any potential name conflicts.
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings localScoping="toplevel"/>
<jaxb:bindings schemaLocation="company.xsd">
<jaxb:bindings node="//xsd:element[#name='employee']/xsd:complexType/xsd:sequence/xsd:element[#name='address']/xsd:complexType">
<jaxb:class name="EmployeeAddress"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
XJC Call
Below is an example of specifying a bindings file when using the XJC command to generate Java classes from an XML schema.
xjc -b binding.xml schema.xsd
For More Information
http://blog.bdoughan.com/2011/07/jaxb-xjc-and-nested-classes.html
The way JAXB normally handles multi valued properties is to provide just a getter and no setter for the List<Whatever>, which returns a mutable list - you're supposed to call the getter to retrieve an initially-empty list and then create the member objects for this list using new in the normal way and add them directly to the list. You can new a static nested class in exactly the same way as a top-level one.
Single-valued properties (not lists) should have been generated with both getter and setter.
This is actually just a comment to Blaise Doughan's answer but I want to post the xml.
If you work with a more complex xsd and the path in the node attribute is getting too long, you can:
<jaxb:bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" version="2.1">
<jaxb:globalBindings localScoping="toplevel"/>
<jaxb:bindings schemaLocation="company.xsd">
<jaxb:bindings node="//xsd:element[#name='employee']">
....
<jaxb:bindings node=".//xsd:element[#name='address']/xsd:complexType">
<jaxb:class name="EmployeeAddress"/>
</jaxb:bindings>
....
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>

JAXB: Convert Javabean Object to XML all properties as XML attributes rather than XML elements (node)

Currently I'm working on converting object to XML, I notice all object properties are listed as XML elements (node) unless you use #XmlAttribute on a particular getter or setting
Just wondering is there a way to automatically convert all object properties as XML attributes in JAXB.
Sample Code:
JAXBContext jc = JAXBContext.newInstance( foo.class );
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.setProperty(Marshaller.JAXB_FRAGMENT, true);
Foo foo = new foo();
foo.setType("type");
foo.setValue("value");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
m.marshal(foo, baos);
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
There currently isn't a way to configure that by default simple properties should map to XML attributes. The following enhancement request has been filed for MOXy to add this behaviour.
Bug 333604 - Enhancement: Provide metadata to default simple properties to attributes (instead of elements)
have you tried using #XmlRootElement on the class top level?
If you are using JAXB for a complex schemata, it's a good idea to define the structure in a XSD:
<xsd:schema targetNamespace="http://myUri"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="parent">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="child" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:long"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="child">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:long"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="code" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
This can be compiled with jaxb-xjc to .java files and you will have the defined xsd:attributes as attributes in Java.

JAXB Generation nillable=true

Using JAXB 2.2.4 I am generating Java code with the following binding file:
<bindings xmlns="http://java.sun.com/xml/ns/jaxb" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<globalBindings generateElementProperty="false">
</globalBindings>
Unfortunately, the generated code has a nillable = true annotation. See the following example:
#XmlElement(name = "ArtID", nillable = true)
protected STEBeitrag artID;
This is the definition of STEBeitrag:
<xsd:complexType name="CT_Beitrag">
<xsd:complexContent>
<xsd:extension base="allgemein:CT_Objekt">
<xsd:sequence>
<xsd:element name="ArtID" type="daten:STE_Beitrag" minOccurs="0" maxOccurs="1" nillable="true"/></xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="STE_Beitrag" abstract="true">
<xsd:simpleContent>
<xsd:extension base="xsd:string"/>
</xsd:simpleContent>
</xsd:complexType>
If I do not set the ArtID in the generated CT_Beitrag object then the unmarshaller produces an output like
<ns:ArtID xsi:nil="true"/>
The element ArtID has an abstract type and therefore this XML output is invalid.
How can I generate code with JAXB that omits the nillable=true in the XmlElement annotation?
By the way, the schema canĀ“t be changed.
I have no solution, but this problem is addressed in bug
http://java.net/jira/browse/JAXB-890
I hope this problem wil be solved.
I don't how to do what you're asking and I'd be surprised if what you're asking is worth the effort.
There are 2 options that jump to my mind:
Change the schema (I know you said you can't but perhaps you can take a local copy of the schema if you can't change it due to it being hosted on a server outside of your control)
Change the generated code... simply change nillable=true to nillable=false

javax.xml.parsers.DocumentBuilder support for complex types

I'm trying to put together some validation code. I am trying to validate against a schema like:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:choice="http://example.com/SimpleChoice" targetNamespace="http://example.com/SimpleChoice" ecore:nsPrefix="choice" ecore:package="com.example.simple.choice">
<xsd:complexType name="Plane">
<xsd:sequence>
<xsd:element name="freightDetails" type="xsd:string" minOccurs="0"/>
<xsd:element name="passengers" type="xsd:int" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
With the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<choice:Plane xmlns:choice="http://example.com/SimpleChoice">
<freightDetails>Boxes</freightDetails>
</choice:Plane>
It seems to complain that there is no element, but I'm trying to find a way to validate against the type. I get the following error:
[Error] :1:100: cvc-elt.1: Cannot find the declaration of element 'choice:Plane'.
When it tries to load the document with the following code:
SchemaFactory factory =
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(schemaFile);
DocumentBuilderFactory parserFactory = DocumentBuilderFactory.newInstance();
parserFactory.setSchema(schema);
parserFactory.setNamespaceAware(true);
DocumentBuilder parser = parserFactory.newDocumentBuilder();
Document document = parser.parse(inputSource);
Validator validator = schema.newValidator();
validator.validate(new DOMSource(document));
It fails when it gets to the:
Document document = parser.parse(inputSource);
Does anyone have any ideas on how I would be able to get this working? (Or a validator that supports this sort of behaviour?)
Thanks
Rob
This is because your schema has defined a type (Plane), but hasn't defined any permitted elements that use that type. A type on its own has no meaning outside of the schema itself.
You need to add an <xsd:element> to your schema. The simplest solution is to use an anonymous complex type within that, something like:
<xsd:element name="Plane">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="freightDetails" type="xsd:string" minOccurs="0"/>
<xsd:element name="passengers" type="xsd:int" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
In your example schema, you have only defined a type named Plane, not an element named Plane. Add an element declaration to your schema and see what happens...

Categories