JAXB - how to detect duplicate nodes while unmarshalling - java

Is it possible to report duplicate nodes while unmarshalling XML without validating it against XML schema? Suppose XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<user xmlns=...>
<firstName>Peter</firstName>
<lastName>Smith</lastName>
<firstName>Eric</firstName>
</user>
and here's the java class (annotations omitted):
public class User {
protected String firstName;
protected String lastName;
.... [getters and setters here]
}
The above XML gets successfully unmarshalled into a User object with firstName=Eric and lastName=Smith. Instead, I want it to fail and report the presence of duplicate element firstName.
We don't use validation against XSD because we support both XML and JSON, so chose to use Bean Validation against annotations contained in JAXB beans (generated java classes). Which works pretty well, but obviously cannot take care of the described problem.

Related

What should be the jaxb annotations for this xml file?

I have a xml file payload which i want to unmarshal using jaxb, I've created a pojo class for unmarshalling and I've defined xml attributes and elements to that pojo, but i'm a little bit confused about namespaces, how to annotate them?
My xml file:
<ns1:ContractLinkEvent xmlns:ns0="http://Enterprise.BizTalk.Canonical.Schemas/v2.0/ESB" xmlns:ns1="http://Enterprise.BizTalk.MCF.Core.Schemas/v2.0/ESB">
<Header xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
</Header>
<ContractLink xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ContractLinkId>1509148</ContractLinkId>
<BillingProfile>
<BillingProfileId>173886</BillingProfileId>
<BillingProfileCode xsi:nil="true"/>
</BillingProfile>
</ContractLink>
</ns1:ContractLinkEvent>
My Jaxb annotated Pojo is:
#XmlRootElement(name = "ContractLinkEvent", namespace="http://Enterprise.BizTalk.Canonical.Schemas/v2.0/ESB")
#XmlAccessorType(XmlAccessType.FIELD)
public class ContractLinkPojo {
#XmlElement(name="Header")
private String Header;
#XmlElement(name="ContractLink")
private String ContractLink;
.
.
.
goes on
I'm getting the following exception while unmarshalling:
java.io.IOException: javax.xml.bind.UnmarshalException
- with linked exception:
[com.sun.istack.SAXParseException2; lineNumber: 1; columnNumber: 1; unexpected element (uri:"http://Enterprise.BizTalk.MCF.Core.Schemas/v2.0/ESB", local:"ContractLinkEvent"). Expected elements are (none)]
I don't think i've defined the namespaces correctly because i haven't yet defined namespaces as i' still confused, any ideas?
EDIT:
This is my routing for unmarshalling
rest("/readXml")
.consumes("application/xml")
.post()
.to("direct:xmlread");
from("direct:xmlread").streamCaching()
.doTry().unmarshal(xmlDataFormat)
.process(readAndInsertXml)
.to("mock:result").end();
}
Namespaces are similar to package names in Java. You use that to have an unique name for XML elements you create so that they do not conflict with other XML elements.
You can define a default XML namespace as:
xmlns="http://Enterprise.BizTalk.Canonical.Schemas/v2.0/ESB"
You can define multiple XML namespace with prefixes as:
xmlns:ns0="..."
xmlns:ns1="..."
In the above ns0 and ns1 are prefixes you created.
In your xml, you have not defined any default namespace. So, I guess you are trying to use namespace prefixes ns0 and ns1 to identity elements. In such a case, you have used ns1 for ContractLinkElement but not < Header >, < ContractLink > or anything else. However, ns0 is not used anywhere and if not required, you can remove that. Please check if this is what you intended to do.
Also, there is no ending tag:
<ns1:ContractLinkEvent
xmlns:ns0="http://Enterprise.BizTalk.Canonical.Schemas/v2.0/ESB"
xmlns:ns1="http://Enterprise.BizTalk.MCF.Core.Schemas/v2.0/ESB">

Include unknown XML snipets in JAXB object

I have an application that reads and writes data from/to XML files to/from a DB using JAXB. The "human readability" of these XML files is one of the top priorities in this project as the idea is that XML files from different DBs can be compared to each other.
Now, I have some tables with attributes that hold XML strings themselves which need to be included to my JAXB objects. As I don't have control over these XML strings and readability is key, I helped myself so far with an XmlAdapter that pretty prints the XML from the DB and wraps it in a CDATA element.
The problem of this solution is that the XML string looks a bit lost within the CDATA element and smart text editors with syntax highlighting don't recognize the XML, so they don't highlight the syntax of that XML.
I was wondering therefore if there's a way to "embed" this XML within an element of my JAXB model as if it would be part of the model. So, I need a kind of XmlAdapter that would parse an XML from a String field of my JAXB class and somehow pass the parsing events to the underlying XMLWriter.
I've spent a lot of time looking for a solution combining JAXB and Stax but didn't succeed. In my view I would need some hook exactly between the JAXB and the Stax layer, so that I can customize the events that JAXB sends to the Stax Writers.
Example:
#XmlRootElement
public class MyJaxbModel {
#XmlAttribute
private Integer anAttribute;
#XmlElement
private String xml;
public MyJaxbModel(Integer anAttribute, String xml) {
this.anAttribute = anAttribute;
this.xml = xml;
}
...
}
Then marshalling the following instance of this class
new MyJaxbModel(999, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><element1><child1>bla</child1><child2>ble</child2></element1>");
should result in the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<MyJaxbModel anAttribute="999">
<xml>
<element1>
<child1>bla</child1>
<child2>ble</child2>
</element1>
</xml>
</MyJaxbModel>
Obviously I need it to work also the other way round, i. e. unmarshalling this XML would return me the MyJaxbModel object including the XML string in the xml field.

how to unmarshall an XML String with Name space to Java Object

hi I got the following response as a string when hitting a client.
I need to unmarshall it so that I can set the values in a Java object and send it back to front end. Kindly help me to convert the following xml string to jaxb object.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:ValidateAustralianAddressResponse xmlns:ns2="http://api.auspost.com.au/ValidateAustralianAddress:v1">
<Address><AddressLine>481 CHURCH ST</AddressLine><SuburbOrPlaceOrLocality>RICHMOND</SuburbOrPlaceOrLocality><StateOrTerritory>VIC</StateOrTerritory><PostCode>3121</PostCode><DeliveryPointIdentifier>55461002</DeliveryPointIdentifier><Country><CountryCode>AU</CountryCode><CountryName>Australia</CountryName></Country></Address>
<ValidAustralianAddress>true</ValidAustralianAddress>
</ns2:ValidateAustralianAddressResponse>
Metadata
Since only the root element is namespace qualified you just need to set the namespace parameter on the #XmlRootElement annotation.
#XmlRootElement(name="ValidateAustralianAddressResponse", namespace="http://api.auspost.com.au/ValidateAustralianAddress:v1")
public class ValidateAustralianAddressResponse {
}
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
Converting XML to Object
You can wrap the XML String in an instance of StringReader and unmarshal that.
For More Information
http://blog.bdoughan.com/2011/08/jaxb-and-java-io-files-streams-readers.html

Ignoring root element using JAXB

I am using Spring and it's REST template to bind XML from a webservice to a domain object using JAXB. The XML returned from the web service is as follows:
<response>
<user>
<id>1</id>
<name>bob</name>
...
</user>
</response>
I have a user class as follows:
public class User {
private String id;
private String name;
}
Is it possible to ignore the "response" element and specify the root element to "user"?
Thanks for any help.
XML that represents your class, has like a root <user> tag.
So:
or you use a java parser to extract the user subtree and after use JAXB,
otherwise you create another class response to mapping your webservice response.
I suggest the second choice.
For informations, when you use whatever XML-binding framework, you must remember the one-to-one relation between class fields and XML tags.

How to detect required XML attributes?

I have an XML content without defined attributes, like this:
<rootElement>
<subElement1/>
</rootElement>
I want to populate this XML content with required attributes defined in XML Schema (XSD) for this XML.
For example, according to XSD subElement1 has required attribute 'id'.
What is the best way (for Java processing) to detect that and add such attributes to XML?
We need to add required attributes and set appropriate values for them.
As a result for example above we need to have the following XML:
<rootElement>
<subElement1 id="some-value"/>
</rootElement>
In the XML schema definition, i.e. XSD file, attributes are optional by default. To make an attribute required, you have to define:
<xs:attribute name="surname" type="xs:string" use="required"/>
You will find a very good introduction on XML and XML Schema Definitions, i.e. XSD, on W3 Schools.
In Java the equivalent of defining a XML schema is using JAXB, i.e. Java API for XML Binding that is included into Java SE. There you would define, e.g.
#XmlRootElement
public class Person { public #XmlAttribute(required=true) String surname; }
Hope this could clarify your question.
I would suggest you to use JAXB for that. Search the Internet for tutorials.
Steps to proceed further with JAXB,
Generate Java files using JAXB by providing the schema
Unmarshal your XML to generated Java classes (beans). Don't do validation or set validation handler here.
Populate those classes with appropriate values. required elements can be found using annotation look up. JAXB annotation for element would look like something, #XmlElement(name = "ElementName", required = true). And an attribute annotation would be something similar to this, #XmlAttribute(required = true)
Marshal your bean back to XML. You can validate your bean using ValidationHandler, while marshalling. Below is the sample code snippet,
marshller = JAXBContext.newInstance(pkgOrClassName).createUnmarshaller();
marshller.setSchema(getSchema(xsd)); // skip this line for unmarshaller
marshller.setEventHandler(new ValidationHandler()); // skip this line for unmarshaller
Use a DOM parser.Has methods to traverse XML trees, access, insert, and delete nodes
I have had the same idea of Cris but I think that with this validator you don't have information about the point in which you have had the error.
I think that you have to create or extend your own validator.

Categories

Resources