Handle both null and empty string values with JAXB - java

I am working with JAXB generated classes from schema and got stuck with following problem. I am trying to handle both null and empty string values with single element.
What I want to achieve is to be able to marshal both null and empty string values and receive the same while unmarshaling.
Element("key1", "value1") -> <element key="key1">value1</element>
Element("key2", "") -> <element key="key2"/>
Element("key3", null) -> <element key="key3" xsi:nil="true"/>
Schema xsd:
<xs:element name="element" nillable="true">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="key" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
So far I get the following results, if value is null or "" I get the same xml:
Element("key1", null) -> <element key="key1"/> -> Element("key1", "")
Element("key2", "") -> <element key="key2"/> -> Element("key2", "")
Any idea if it is even possible with this xml structure? The only idea I have is to change structure to something like this, but it will generate more useless classes to deal with.
<element>
<key>key1</key>
<value>value1</value>
</element>
Edit
With given schema change it works as expected. The question if it is possible with original structure is still open.
<xs:element name="element">
<xs:complexType>
<xs:sequence>
<xs:element name="key" type="xs:string"/>
<xs:element name="value" type="xs:string" nillable="true"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<element>
<key>key1</key>
<value xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</element>
<element>
<key>key2</key>
<value/>
</element>

Related

How do I validate XML using XSD?

I've made a mistake but I can't see what it is...
I'm trying to validate a piece of XML (a SOAP message) using Java, against an XSD. It's failing on what I think should be a valid piece of XML/message with the Exception:
org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'tns:Envelope'.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203).
Note 1: I've debugged xmlSource and xsdSource and they contain the expected Readers with the expected Strings.
Note 2: The content of the XML cannot be changed. I am a consumer only.
Note 3: As the XML is a SOAP message I grabbed the XSD from the Web. This therefore could be incorrect.
Note 4: I've simplified the incoming message for brevity but get the same error with the "actual" message.
Java
#Override
public boolean isMessageValid(final String xml) {
try {
final Source xmlSource = getStreamSource(xml);
final Source xsdSource = getStreamSource(fileService.getResourceFileAsString("xsd/soap-envelope.xsd"));
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
final Schema schema = schemaFactory.newSchema(xsdSource);
final Validator validator = schema.newValidator();
validator.validate(xmlSource);
return true;
} catch (final Exception ex) {
LOG.error("Exception whilst validating message", ex);
return false;
}
}
private StreamSource getStreamSource(final String xml) {
return new StreamSource(new StringReader(xml));
}
XML
<?xml version="1.0" encoding="UTF-8"?>
<tns:Envelope xsi:schemaLocation="http://www.w3.org/2001/12/soap-envelope soap-envelope.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://www.w3.org/2001/12/soap-envelope">
<tns:Body>
<!-- snipped -->
<someThing>else</someThing>
</tns:Body>
</tns:Envelope>
XSD
<?xml version='1.0' encoding='UTF-8' ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/"
targetNamespace="http://schemas.xmlsoap.org/soap/envelope/" >
<!-- Envelope, header and body -->
<xs:element name="Envelope" type="tns:Envelope" />
<xs:complexType name="Envelope" >
<xs:sequence>
<xs:element ref="tns:Header" minOccurs="0" />
<xs:element ref="tns:Body" minOccurs="1" />
<xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##other" processContents="lax" />
</xs:complexType>
<xs:element name="Header" type="tns:Header" />
<xs:complexType name="Header" >
<xs:sequence>
<xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##other" processContents="lax" />
</xs:complexType>
<xs:element name="Body" type="tns:Body" />
<xs:complexType name="Body" >
<xs:sequence>
<xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##any" processContents="lax" >
<xs:annotation>
<xs:documentation>
Prose in the spec does not specify that attributes are allowed on the Body element
</xs:documentation>
</xs:annotation>
</xs:anyAttribute>
</xs:complexType>
<!-- Global Attributes. The following attributes are intended to be usable via qualified attribute names on any complex type referencing them. -->
<xs:attribute name="mustUnderstand" >
<xs:simpleType>
<xs:restriction base='xs:boolean'>
<xs:pattern value='0|1' />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="actor" type="xs:anyURI" />
<xs:simpleType name="encodingStyle" >
<xs:annotation>
<xs:documentation>
'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern described in SOAP specification
</xs:documentation>
</xs:annotation>
<xs:list itemType="xs:anyURI" />
</xs:simpleType>
<xs:attribute name="encodingStyle" type="tns:encodingStyle" />
<xs:attributeGroup name="encodingStyle" >
<xs:attribute ref="tns:encodingStyle" />
</xs:attributeGroup>
<xs:element name="Fault" type="tns:Fault" />
<xs:complexType name="Fault" final="extension" >
<xs:annotation>
<xs:documentation>
Fault reporting structure
</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="faultcode" type="xs:QName" />
<xs:element name="faultstring" type="xs:string" />
<xs:element name="faultactor" type="xs:anyURI" minOccurs="0" />
<xs:element name="detail" type="tns:detail" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="detail">
<xs:sequence>
<xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##any" processContents="lax" />
</xs:complexType>
</xs:schema>
I reckon I've done something noddy.
Thanks in advance
The prefix "tns" is bound to different namespaces in the source and in the schema. So this is not a schema for the vocabulary of your source document.

JAXB Unmarshalling of XML into HashMap

I have a complex XML Structure as below which needs to be unmarshalled:
<abc>
<pqr>
<attribute>name</attribute>
<entry>
<priorityKey>
<class>123</class>
<reason>abc</reason>
</prioritykey>
<priority> 1 </priority>
</entry>
<entry>
<prioritykey>
<class>456</class>
<reason>abc1</reason>
</prioritykey>
<priority>2</priority>
</entry>
</pqr>
<pqr>
'''
'''
</pqr>
</abc>
abc is the root node. It can have multiple pqr elements. Each pqr element has one attribute node and multiple entry nodes. So I believe it will be of type HashMap(entry,attribute).
Each entry in turn has prioritykey and priority which I believe will be of type HashMap (prioritykey,priority).
Need to unmarshall this xml but not getting how to configure the XMLAdapter
Here, create the contract first. I created one as part of my learning process...
<xs:element name="abc" type="abcType" />
<xs:complexType name="abcType">
<xs:sequence>
<xs:element name="pqr" type="pqrType" minOccurs="1"
maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="pqrType">
<xs:sequence>
<xs:element name="attribute" type="xs:string" minOccurs="1"
maxOccurs="1" />
<xs:element name="entry" type="entryType" minOccurs="1"
maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="entryType" >
<xs:sequence>
<xs:element name="priorityKey" type="priorityKeyType"
minOccurs="1" maxOccurs="1" />
<xs:element name="priority" type="xs:int" minOccurs="1"
maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="priorityKeyType">
<xs:sequence>
<xs:element name="class" type="xs:int" minOccurs="1"
maxOccurs="1" />
<xs:element name="reason" type="xs:string" minOccurs="1"
maxOccurs="1" />
</xs:sequence>
</xs:complexType>
Copy and save this in an fileName.xsd file.
Now, generate JAXB classes or artifacts. If you are using an IDE, then this is simple. Click on the fileName.xsd file and you should get some option to generate JAXB artifacts. Otherwise, you can always use jaxb-xjc.jar present in the lib present in the JAXB download.
Command for the same - java -jar jaxb-xjc.jar fileName.xsd.
As the artifacts are in place, you can use them to unmarshall the xml content in question.

XSD polymorphism doesn't work with JAXP?

I had an XML like this:
<members>
<member>
<name>john</name>
<properties>
<age>20</age>
<address>20</address>
</properties>
</member>
<member>
<name>kayla</name>
<properties>
<gender>female</gender>
<address>20</address>
</properties>
</member>
</members>
There will be several member nodes in here, they all share a <name> node and a properties node. However, the elements in the properties are different, either age+address or gender+address are valid choices.
I tried to write an XSD like this:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="members" type="membersType"/>
<xs:complexType name="membersType">
<xs:sequence>
<xs:element type="memberType" name="member" maxOccurs="unbounded" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="memberType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element type="propertiesType" name="properties"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="propertiesType">
<xs:sequence>
<xs:element type="xs:string" name="address"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="propertiesTypeA">
<xs:complexContent>
<xs:extension base="propertiesType">
<xs:sequence>
<xs:element type="xs:string" name="age" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="propertiesTypeB">
<xs:complexContent>
<xs:extension base="propertiesType">
<xs:sequence>
<xs:element type="xs:string" name="gender" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
I hope there can be something like polymorphism in here, but when I validate using JAXP, that doesn't work.
I also tried to use the group feature, but there's a same address element in the 2 groups, thus it's still not available.
So, how should I design the XSD?
Your Current XML Schema
With your XML Schema the way it is, your XML would need to look like the following:
<members xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<member>
<name>john</name>
<properties xsi:type="propertiesTypeA">
<address>20</address>
<age>20</age>
</properties>
</member>
<member>
<name>kayla</name>
<properties xsi:type="propertiesTypeB">
<address>20</address>
<gender>female</gender>
</properties>
</member>
</members>
Note the following things:
The inherited attributes need to appear first.
You need to use the xsi:type attribute to override the element type to be the subtype.
What You Probably Want
Specifying a "choice" in your XML Schema would probably be a better fit for you. You would no longer need the sub types.
<xs:complexType name="propertiesType">
<xs:sequence>
<xs:choice>
<xs:element type="xs:string" name="age"/>
<xs:element type="xs:string" name="gender"/>
</xs:choice>
<xs:element type="xs:string" name="address"/>
</xs:sequence>
</xs:complexType>
You can use like that below
<complexType name="MemberDTO">
<complexContent>
<extension base="tns:MemberEnumValueDTO">
<sequence />
</extension>
</complexContent>
</complexType>
<complexType name="MemberEnumValueDTO">
<sequence>
<element name="name" type="tns:NameEnumTextType" minOccurs="1"
maxOccurs="1" />
</sequence>
</complexType>
<simpleType name="NameEnumTextType">
<restriction base="string">
<enumeration value="john" />
<enumeration value="kayla" />
</restriction>
</simpleType>

How to get the right jaxb object when having two elements under one list (jaxbObj.getAoAndBo() )?

I'm using Java and JAXB for parsing a xml file.
I had a schema(xsd) that contains secquence element that contains two inner sequence element.
the xsd looks something like the following:
...
<xs:element name="product_list">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:element maxOccurs="unbounded" name="product">
<xs:complexType>
<xs:sequence />
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="release" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element maxOccurs="unbounded" name="info">
...
</xs:complexType>
</xs:element>
</xs:schema>
After unmarshalling, when I want to fetch the list of "product", I get:
product_list.getProductsAndInfo() => type: List<Object>
How can I get the information from the "product" object? (e.g product_list.getProduct)
for getting the needed object information there is a need to use casting on the list of objects, for example:
ProductList pl = new ProductList(); // xml main object/element
pl= unmarshallfunc();
((ProductList.Product)pl.getProductAndInfo().get(0)).getName(); //type: List cast to Product.

validating xml data in java

My transaction xml is shown below
<?xml version= "1.0"?>
<transactionlist>
<transaction action="c">
<transactionid>t004</transactionid>
<transactiondate>11/06/2013</transactiondate>
<merchantdetails>Sony wholesale Dealer</merchantdetails>
<itempurchased>3</itempurchased>
<amount>40399</amount>
<description>sony laptops</description>
</transaction>
<transaction action="d">
<transactionid>t003</transactionid>
</transaction>
<transaction action="u">
<transactionid>T001</transactionid>
<transactiondate>20/08/2013</transactiondate>
<merchantdetails>samsung Axses</merchantdetails>
<itempurchased>1</itempurchased>
<amount>40000</amount>
<description>samsung smart phone</description>
</transaction>
</transactionlist>
I have parsed the element itempurchased in above xml and stored it in integer variable. How to validate itempurchased only for numbers. that is i want to check whether itempurchased is number. pls provide suggestions
the best way should be validate xml against an xsd, where itempurchased would be of type xsd:int
below the xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="transactionlist">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="transaction">
<xs:complexType>
<xs:sequence>
<xs:element name="transactionid" type="xs:string" />
<xs:element minOccurs="0" name="transactiondate" type="xs:string" />
<xs:element minOccurs="0" name="merchantdetails" type="xs:string" />
<xs:element minOccurs="0" name="itempurchased" type="xs:int" />
<xs:element minOccurs="0" name="amount" type="xs:int" />
<xs:element minOccurs="0" name="description" type="xs:string" />
</xs:sequence>
<xs:attribute name="action" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Here Validating XML against XSD the code for validate xml against xsd
If you are marshaling your xml to a java bean then you may try using the Java6 Bean Validation Framework. Read more about it here:
http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html
It is as simple as putting an annotation on your bean:
public class MyXMLBean {
#Max(10)
#Min(5)
private int itempurchased;
}
The above bean will allow setting the value of itempurchased between min and max values mentioned in the annotations.

Categories