Here's the xml I receive
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header/>
<soap-env:Body RequestId="1503948112779" Transaction="HotelResNotifRS">
<OTA_HotelResNotifRS xmlns="http://www.opentravel.org/OTA/2003/05" TimeStamp="2017-08-28T19:21:54+00:00" Version="1.003">
<Errors>
<Error Code="450" Language="en-us" ShortText="Unable to process " Status="NotProcessed" Type="3">Discrepancy between ResGuests and GuestCounts</Error>
</Errors>
</OTA_HotelResNotifRS>
</soap-env:Body>
</soap-env:Envelope>
This gets pseudo unmarshalled into an OTAHotelResNotifRS object which you can get .getErrors() on. The issue is that there was no type associated with this bit, so it comes back as an Object which is in the form of an ElementNSImpl. I don't control the OTAHotelResNotifRS object, so my best bet is to unmarshal the .getErrors() object into a pojo of my own. This is my attempt.
#XmlRootElement(name = "Errors")
#XmlAccessorType(XmlAccessType.FIELD)
public class CustomErrorsType {
#XmlAnyElement(lax = true)
private String[] errors;
public String[] getErrors() {
return errors;
}
}
This is the code used to try to unmarshal it into my CustomErrorsType object
Object errorsType = otaHotelResNotifRS.getErrors();
JAXBContext context = JAXBContext.newInstance(CustomErrorsType.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
CustomErrorsType customErrorsType = (CustomErrorsType)unmarshaller.unmarshal((Node)errorsType);
It throws the following exception when calling unmarshal
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.opentravel.org/OTA/2003/05", local:"Errors"). Expected elements are <{}Errors>
Any thoughts? I'm pretty weak when it comes to xml unmarshalling.
You are ignoring the XML namespace in the response, as defined in the xmlns attribute. See https://www.w3.org/TR/xml-names/ for a full explanation of namespaces and the attributes which define them.
A standard notation for describing an XML name with its namespace is {namespace-uri}local-name. So the exception is literally telling you that your CustomErrorsType expects an element with the local name Errors and an empty namespace ({}), but instead it encountered an element with the local name Errors and a namespace which is http://www.opentravel.org/OTA/2003/05.
Try changing this:
#XmlRootElement(name = "Errors")
to this:
#XmlRootElement(name = "Errors", namespace = "http://www.opentravel.org/OTA/2003/05")
As a side note, if you have access to the WSDL which defines the SOAP service, you can make your task considerably easier by invoking the standard JDK tool wsimport with the WSDL’s location as an argument. All marshalling will be implicitly taken care of by the generated Service and Port classes.
Related
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">
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
I have generated my class with xjc and wsimport tools. I have an Object with all the attributes and elements I need. I have a SignatureType Object that must contain the sign of the data i will send.
My root object looks like this:
#XmlRootElement(name = "MensajePesadoFirmadoTipo")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "MensajePesadoFirmadoTipo", propOrder = {
"signature"
})
public class MensajePesadoFirmadoTipo
extends MensajePesadoTipo
{
#XmlElement(name = "Signature", namespace = "http://www.w3.org/2000/09/xmldsig#", required = true)
protected SignatureType signature;
...
The process I'm doing is: Fill all the information of MensajePesadoFirmadoTipo except SignatureType. Next, I unmarshall to xml and I use Java XMl Signature API. Then I marshall again and I call the WS.
I have an integrity problem of sign because the signed XML It is not the same when I marshall before call the WS.
XML Signed (signed ok):
<ns1:MensajePesadoFirmadoTipo xmlns:ns1="urn:correos.es:ape.sne.datos.1.0" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" modalidad="1" ns1:idRef="idRef" ns1:idUnico="idUnico" tipo="NOTIFICACION">
...
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
...
</ds:Signature></ns1:MensajePesadoFirmadoTipo>
RESULTING XML AFTER MARSHAL (integrity problem, de XML has change):
<ns1:MensajePesadoFirmadoTipo xmlns:ns1="urn:correos.es:ape.sne.datos.1.0" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" modalidad="1" ns1:idRef="idRef" tipo="NOTIFICACION" ns1:idUnico="idUnico">
...
<ns2:Signature>
...
</ns2:Signature>
</ns1:MensajePesadoFirmadoTipo>
How can I make this process ok?? Please help!!!
Thanks a lot!!!
You didn't put the details of your signature (the complete ds:Signature nodes). This would help.
Depending on whether you are using XML Canonicalization transforms or not your signature may be valid even if the XML after marshal is not the same as before. XML canonicalization (google it if not familiar with it) is a mean used to "canonicalize" an XML input before computing the signature. For example, this includes ordering the attribute so that after the canonicalization, MensajePesadoFirmadoTipo in your both snippets will give the same input to the signature computation.
Hope this helps.
Moez
I have a JAXB object (ProductRequest) that represents an XML document for a webservice request. Assume it looks something like this:
<ProductRequest>
<getProducts/>
</ProductRequests>
For the response, the JAXB object (ProductResponse) will represent an XML document as shown below:
<ProductResponse>
<productId>1</productId>
<productName>Oranges</productName>
<productId>2</productId>
<productName>Apples</productName>
</ProductResponse>
Using Spring-WS, i can send the web service the request using two approaches
Using the JAXB object
ProductRequest productRequest = new productRequest();
ProductResponse productResponse = (ProductResponse) webServiceTemplate
.marshalSendAndReceive(productRequest);
Using plain XML/DOM
DOMResult domresult = new DOMResult();
webServiceTemplate.sendSourceAndReceiveToResult(source, domresult); //source represents the XML document as a DOMSource object
Element responseElement = (Element) domresult.getNode().getFirstChild();
When i try both approaches, the results are different. The first approach using the JAXB object the result is
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProductResponse xmlns="http://mySchema">
<productId>1</productId>
<productName>Oranges</productName>
<productId>2</productId>
<productName>Apples</productName>
</ProductResponse>
The second approach using the XML Dom object the result is (Includes namespaces)
<?xml version="1.0" encoding="UTF-8"?>
<ns2:ProductResponse xmlns:ns2="http://mySchema">
<ns2:productId>1</ns2:productId>
<ns2:productName>Oranges</ns2:productName>
<ns2:productId>2</ns2:productId>
<ns2:productName>Apples</ns2:productName>
</ns2:ProductResponse>
The header for the schema used for the response on the web service is declared as:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:z="http://mySchema"
targetNamespace="http://mySchema"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
-- Schema elements
</xs:schema>
There are two differences in the response
The first line on the response from JAXB request includes the entry standalone="yes">
The elements on the JAXB version are not prefixed with the namespace
Shouldn't the response with elements prefixed with the schema have used "z" (as defined in the schema) instead of ns2?
I don't understand what could be causing this difference given that they are both calling the same web service which generates the response based on the same schema. Any ideas?
The XML content is the same but the difference in the format of the XML is giving me problems as i cant use String.equals() to compare the two.
The responses are the same, just that one isn't qualified with a namespace.
On a side note, your XML design looks a bit flaky. Might be better like this;
<ProductResponse>
<product>
<id>1</id>
<name>Oranges</name>
</product>
<product>
<id>2</id>
<name>Apples</name>
</product>
</ProductResponse>
Why? Because you shouldn't rely on the order of elements.
The result is the same. ns2 is just the prefix for the namespace, jaxb uses default namespace and XML Dom uses a the prefix ns2. The xml files are equivalent, and both of them are valid against that schema. You can read more about XML namespace here.
A web service is returning an object defined by the WSDL to be:
<s:complexType mixed="true"><s:sequence><s:any/></s:sequence></s:complexType>
When I print out this object's class info, it comes up as:
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
But I need to unmarshall this object as an object of the following class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"info",
"availability",
"rateDetails",
"reservation",
"cancellation",
"error" })
#XmlRootElement(name = "ArnResponse")
public class ArnResponse { }
I know the response is correct, since I know how to marshall this object's XML:
Marshaller m = jc.createMarshaller();
m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
m.marshal(rootResponse, System.out);
Which prints out:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:SubmitRequestDocResponse xmlns:ns2="http://tripauthority.com/hotel">
<ns2:SubmitRequestDocResult>
<!-- below is the object I'm trying to unmarshall -->
<ArnResponse>
<Info />
<Availability>
<!-- etc-->
</Availability>
</ArnResponse>
</ns2:SubmitRequestDocResult>
</ns2:SubmitRequestDocResponse>
How can I turn the ElementNSImpl object I'm seeing into the ArnResponse object I know it represents?
Additionally, I'm running on AppEngine, where file access is restricted.
Thanks for any help
Update:
I've added the #XmlAnyElement(lax=true) annotation, like so:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"content"
})
#XmlSeeAlso(ArnResponse.class)
public static class SubmitRequestDocResult {
#XmlMixed
#XmlAnyElement(lax = true)
protected List<Object> content;
But it doesn't make any difference.
Is this something to do with the fact that the content is a List?
Here's the code where I'm trying to access the content after getting it back from the server:
List list = rootResponse.getSubmitRequestDocResult().getContent();
for (Object o : list) {
ArnResponse response = (ArnResponse) o;
System.out.println(response);
}
Which has the output:
Jan 31, 2012 10:04:14 AM
com.districthp.core.server.ws.alliance.AllianceApi getRates SEVERE:
com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to
com.districthp.core.server.ws.alliance.response.ArnResponse
Answer:
axtavt's answer did the trick. This worked:
Object content = ((List)result.getContent()).get(0);
JAXBContext context = JAXBContext.newInstance(ArnResponse.class);
Unmarshaller um = context.createUnmarshaller();
ArnResponse response = (ArnResponse)um.unmarshal((Node)content);
System.out.println("response: " + response);
You can pass that object to Unmarshaller.unmarshal(Node), it should be able to unmarshal it.
You can use #XmlAnyElement(lax=true). This will convert XML with known root elements (#XmlRootElement or #XmlElementDecl) to domain objects. For an example see:
http://blog.bdoughan.com/2010/08/using-xmlanyelement-to-build-generic.html
From what I've found with working with XML, anyType could represent any object, so the closest thing you can map it back to is java.lang.Object. (Besides that fact that anyType could technically be a security hole, allowing somebody to inject anything, including a malicious binary into that spot, and nothing will stop it since your schema allows it.)
You are best off to change your schema to allow a mapping to your custom object. This is cleaner both from a programming perspective, a consumption perspective, and a security perspective.
Pending you can't do that, I would recommend storing the type as an attribute of your element. You may need to write custom code then to help you convert the anyType back to that object, but at least you know its type then.
My two cents based off of my experience (mostly in the realm of integration).