JAXB creating empty objects - java

I have an XML based on XSD. The xsd is
<xs:complexType name="objectOneType" mixed="true">
<xs:sequence>
<xs:element ref="nestedObject" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="attribute" type="xs:string" use="optional"/>
</xs:complexType>
Now, when i receive this XML
<xmlresponse>
<xmlrequest>
<objectone attribute="changed" />
</xmlrequest>
<xmlsrc>
<objectone attribute="changed" />
</xmlsrc>
</xmlresponse>
In this XML, the object OBjECTONE doesn't have an object named NESTEDOBJECT. now instead of ignoring it, it is inserting a an empty object in OBJECTONE which is of STRING type. so when my code tries to process OBJECTONE assuming it has an object type NESTEDOBJECT. It throws classcast exception.
Any idea?
P.S: Kill me sicne i am using JAXB 1.2

The key is the annotation mixed="true". With this annotation structures like
<content>
<italic>Italic</italic> ist not <underline>underline</underline>
</content>
are possible. Internally JAXB creates a List<Object> insideContent which would be in the upper case classes of: String, Italic, String, Underline and String. I assume that you output your XML with something like Marshaller.JAXB_FORMATTED_OUTPUT which inserts a newline and tabulator for the formatted output. You can either:
Don't use formatted output: <content><italic>This</italic> is a <underline>test</underline></content> should not have a leading or trailing String
Don't use mixed="true"
Test your child class with instanceOf while iterating the List<Object>

Related

With JAXB how do we avoid JAXBElement for mixed complexType

When a complexType is marked with mixed="true" in XSD, the generated Java class produces a List with either a "String" type or a JAXBElement wrapper around the actual value. Without mixed="true", the JAXBElement is not produced and the value is directly mapped to the type class. Eg:
<xs:complexType name="div" mixed="true">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="a" minOccurs="0" maxOccurs="unbounded" type="anchorType"/>
</xs:sequence>
...
</complexType>
Here the div type would contain a a List getContent() method that either returns a String or a JAXBElement for AnchorType.
I wanted it to return just either String or AnchorType without the JAXBElement.
Consider this XML which contains a div tag and some child tags:
<parentTag>
<div>myPlainTextValue<a/><a/></div>
</parentTag>
But the mixed content in an XML document does not necessarily occur in a single block.
This XML is also valid according to the XSD:
<parentTag>
<div>my<a/>Plain<a/>Text<a/>Value</div>
</parentTag>
In both cases, the text content of the tag 'div' is 'myPlainTextValue'. But in the second example it is distributed in 4 smaller chunks in between the child tags. JAXB needs to generate code that can handle the general case, so I would not expect it to have a special mode for your case.

XML Validation against XSD : cvc-complex-type.2.4.a

Currently I am using javax.xml.validation.Validator to validate xml against given xsd. I have set custom error handler to get all exceptions instead of exiting at the first exception.
Sample xsd:
<xs:element type="xs:string" name="att1"/>
<xs:element type="xs:string" name="att2"/>
<xs:element type="xs:string" name="att3"/>
<xs:element type="xs:string" name="att4"/>
In xml if att2 and att3 values are not there, I am getting below exception.
cvc-complex-type.2.4.a: Invalid content was found starting with element 'att4'. One of '{"https://******":att2}' is expected.
But I need exception to be like this i.e. both att2 and att3 should be shown in expected list.
cvc-complex-type.2.4.a: Invalid content was found starting with element 'att4'. One of '{"https://******":att2, "https://******":att3}' is expected.
How can I achieve this?
As a second element, the xsd specifies att2, put you provided att4.
As a third element, the xsd specifies att3 but you didn't provide any element.
You can try to set the elements att2 and/or att3 as optional with :
minOccurs="0" maxOccurs="1"
If it doesn't work, you can try :
<xs:element type="xs:string" name="att1"/>
<xs:choice minOccurs="0" maxOccurs="2">
<xs:sequence>
<xs:element maxOccurs="1" name="att2" type="xs:string" />
<xs:element maxOccurs="1" name="att3" type="xs:string" />
</xs:sequence>
</xs:choice>
<xs:element type="xs:string" name="att4"/>
The validator is implemented as a finite state machine. It computes the permitted transitions from one state to another. After reading an att1 element, the only permitted transition is to an att2, and that's what it's telling you. It's not smart enough to explore the entire finite state machine and work out that at att4 would be valid if there was an att2 and then an att3.
The Saxon validator does a little better than this, but only a little: it won't give you what you're looking for here.
If it's any consolation, XSD validators generally do a lot better than regex engines (which is essentially what they are); regex engines generally just tell you that the input isn't matched by the regex, and give you no clue why.

Declare collection of varying types in XSD

I have an API that returns an XML set of objects, these objects are guaranteed to be representable by an abstract class, however they are not directly accessible as a list since they take the following form:
<Response Timestamp="2019-02-06T13:16:32">
<TypeA [xml attributes]...>
...
</TypeA>
<TypeB ...>
...
</TypeB>
... (Different repeating elements)
</Response>
Due to company practices I am to write the model for this feed using an XSD, which is parsed by JaxB to generate the source files. However, short of individually declaring the Types as possible elements of the collection (which I do not want to do for obvious reasons), I do not know how to approach this and get the child elements of the response as one single collection.
XSD for the response.
<xs:complexType name="Response">
<xs:sequence>
<xs:element name="Types" type="model:AbstractType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
Example element type
public class TypeA extends AbstractType
{
//Generated source
}
How do I designate in the XSD that any TypeX object, extending AbstractType, should go in to the Types collection from the response?
Happy to provide any necessary further information, as long as it is not something I am not allowed to share.
Saw this related question however it accepted any element, and the restriction was based on name, whereas for this I would ideally like to validate that the elements collected are valid TypeX objects.
Assuming you have defined each TypeX as AbstractType extension in the XSD:
<xsd:complexType name="TypeX">
<xsd:complexContent>
<xsd:extension base="AbstractType">
...
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
Then you can do:
1) XSD choice, if you really want to use different XML element names for each TypeX (not recommended because requires to modify the Response type whenever you add a new AbstractType subtype):
<xs:complexType name="Response">
<xs:choice maxOccurs="unbounded">
<xs:element name="typeA" type="model:TypeA" />
<xs:element name="typeB" type="model:TypeB" />
</xs:sequence>
</xs:complexType>
2) XML polymorphism (close to what you suggest), more generic although changes the XML form:
<xs:complexType name="Response">
<xs:sequence>
<xs:element name="something" type="model:AbstractType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
(Replace 'something' with some meaningful name depending on what AbstractType represents actually.)
The xml would look like this:
<Response Timestamp="2019-02-06T13:16:32" xmlns="...">
<something xsi:type="TypeA" [xml attributes]...>
...
</something>
<something xsi:type="TypeB" ...>
...
</something>
... (Different repeating elements)
</Response>
In both cases, I recommend you use JAXB RI extension for simpler/better binding mode (ยง 3.1.6), or equivalent extension, that simplifies the generated code and convert generated fields to plural form when necessary.
Then the generated code in Response class (notice the plural form):
List<AbstractType> somethings; .

mapping anyType to String in JAXB

I need to generate java classes for 3rd party schema I received (and unfortunately cannot modify), that looks like that:
...
<xs:element name="ARec">
<xs:complexType>
<xs:sequence>
<xs:element name="ARecTyp" />
<xs:element name="AGrp" />
<xs:element name="AGrpId" />
...
And using maven-jaxb2-plugin, I'm able to do that, but my classes has all them members declared as
#XmlElement(name = "ARecTyp", required = true)
protected Object aRecTyp;
(which, I believe, is correct and default mapping of xs:anyType). But in reality these elements are all texts, and I'd like to have it bound to java.lang.String, not java.lang.Object.
I tried to add
<globalBindings>
<javaType name="java.lang.String" xmlType="xs:anyType"/>
</globalBindings>
but that won't work, as xs:anyType is complexType, not simpleType, so I'm getting exception. Is there any other way of mapping such xs:element without specified type to String instead of Object?

JAXB POJO's generated from WDSL seem cumbersome to use

I'm not new to SOAP and turning WSDL's into POJO's. I've managed to connect to the web service and retrieve the data I need.
The problem rests in traversing the object hierarchy that the WSDL created.
Most of the variables stored in the generated classes are of the type JAXBElement<NameOfJavaClassHere>. So when ever I want that object I need to issue a call like ListOfEntitiesType loe = ents.getListOfEntities().getValue(); The .getValue() is where I have my issue.
Does there exist a way of making this a smoother integration? If I have to keep doing a getValue() it's going to be a death of 1000 cuts.
It feels like they left in a level of indirection at the client level that they didn't need to.
I've tried to unmarshal the xml with a JAXBContent object, there are a lot of examples on the net on how to do this, but it didn't work in this case. My object came out as null.
Should the WSDL not make POJO's that don't need all this casting about with generics?
Did I use the wrong settings on my wsimport command that came with java 1.7?
Should I use a different wsimport-ish program altogether to generate my POJO's?
If I have to stick with the .getValue() thing, I think I'd much rather make an xpath interface to the raw XML or turn the whole thing into Hashtables and ArrayLists than deal with this.
At least then, I'd have direct access to the info I want.
This XML schema has probably been developed under the influence of some bureaucratic rule. Consider, e.g.
<xs:element minOccurs="0" name="DUNSNumber" nillable="true" type="xs:string"/>
which results in a field
protected JAXBElement<String> dunsNumber;
causing the highly circumstantial get and set procedure you have (rightly) complained about. - What does the XML Schema entry mean? It says the element is a string, can be omitted and it must be possible to distinguish between an empty string and an absent string even when the element is present.
Here is a little experiment:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0">
<xs:element name="root" type="RootType"/>
<xs:complexType name="RootType">
<xs:sequence>
<xs:element maxOccurs="unbounded" name="elem" type="ElemType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ElemType">
<xs:sequence>
<xs:element minOccurs="0" name="str01" type="xs:string"/>
<xs:element minOccurs="1" name="str1" type="xs:string"/>
<!-- The following element compiles to a JAXBElement<String> -->
<xs:element minOccurs="0" name="str01nil" nillable="true" type="xs:string"/>
<xs:element minOccurs="1" name="str1nil" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
And here is an XML file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<elem>
<str01></str01>
<str1></str1>
<str01nil></str01nil>
<str1nil></str1nil>
</elem>
<elem>
<str1>must be there</str1>
<str01nil xsi:nil="true"></str01nil>
<str1nil>must be there</str1nil>
</elem>
</root>
And here is what is printed by an unmarshalling routine:
str01 []
str1 []
str01Nil []
str1Nil []
str01 [null]
str1 [must be there]
str01Nil [null]
str1Nil [must be there]
What happens if you omit <str01nil xsi:nil="true"></str01nil> completely? The outcome will be the same, method JAXBElement.getValue() will return null, and that's it.
Now (sorry for the length of the answer) we can discuss what you can do to return to "sane" Java code generated from the XML schema. I would simply remove nillable="true" and use the resulting code. If you marshal, a null in a field will not produce an element. On unmarshal, there's the weak chance that you see an empty element with an xsi:nil="true". (It is essential to retain minOccurs="0", though.)
There are a few use cases where a JAXBElement is required to be able to round trip XML as defined in the XML Schema. If you are seeing alot of JAXBElement in your generated model then several of these conditions are probably true.
An element is both nillable="true" and minOccurs="0". In this case what does null on the mapped field/property mean? When the property is JAXBElement a null value means the element isn't present and a JAXBElement wrapping null means an XML element with xsi:nil="true".
There are 2 global elements with the same named complex type. Since in JAXB classes correspond to complex types a way is needed to capture which root element was encountered.
http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html
There is a choice structure where either foo or bar elements can occur and they are the same type. Here a JAXBElement is required because simply encountering a String value isn't enough to indicate which element should be marshalled.
An element with xsi:nil is encountered in the document that contains attributes. In this example the object corresponding to that element can still be unmarshalled to hold the attribute values, but JAXBElement can stil indicate that the element was null.
Mechanisms to Reduce the Number of JAXBElement in the Model
Binding file with generateElementProperty set to false.
<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb" xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
version="2.1">
<globalBindings>
<generateElementProperty>false</generateElementProperty>
</globalBindings>
</bindings>
Simple plugin for XJC - https://jaxb.java.net/2.1.2/docs/vendorCustomizations.html#simple
<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb" xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
version="2.1">
<globalBindings>
<xjc:simple />
</globalBindings>
</bindings>

Categories

Resources