Should a web service response include empty values? - web-services

When invoking a web service I get a dynamic response in XML format.
So response could be :
<response>
<test1>test1</test1>
<test2>test1</test2>
<test3>test1</test3>
<test4>test1</test4>
</response>
or :
<response>
<test1>test1</test1>
<test2>test1</test2>
</response>
But I think the response should be static in order for the Java class to be unmarshalled correctly from the XML.
So instead of
<response>
<test1>test1</test1>
<test2>test1</test2>
</response>
This should be :
<response>
<test1>test1</test1>
<test2>test1</test2>
<test3></test3>
<test4></test4>
</response>
This means I can now handle the response and check for missing data.
Am I correct in my thinking?

Default Null Representation
By default a JAXB (JSR-222) implementation will treat a property as an optional element. As such null values are represented as the element being absent from the document.
Alternate Representation of Null
Alternatively you have null represented by including the xsi:nil="true" attribute on it. This is achieved by annotating your property with #XmlElement(nillable=true).
<date xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
Invalid Null Representation
An empty element is not a valid representation for null. It will be treated as an empty String which will be invalid for all non-String types.
<date/>
For More Information
http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html
Update
SO test1 test1 is
a valid response but the fields test3 & test4 will be set to null ?
What happens is that nothing is done for the fields/properties that correspond to absent nodes. They will keep there default values, which are by default initialized to null.
Java Model (Root)
In the model class below I have initialed the fields to have values that are not null.
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Root {
#XmlElement
String foo = "Hello";
String bar = "World";
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
Demo
The document being marshalled <root/> does not have any elements corresponding to the mapped fields/properties in the model class.
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader xml = new StringReader("<root/>");
Root root = (Root) unmarshaller.unmarshal(xml);
System.out.println(root.foo);
System.out.println(root.bar);
}
}
Output
We see that the default values were output. This shows that a set operation was not performed for the absent nodes.
Hello
World

Refer to JAXB Marshalling with null fields
and also What's the purpose of minOccurs, nillable and restriction?
Use #XmlElement(nillable = true) for those null/blank value fields to display; but pay particular attention to your date fields.

Related

Camel XSLT transformation

I am trying to use the XSLT component to do dynamic transformation from XML. Is it possible to pass in a java variable in the URI as XSLT template?
For example:
from("direct:foo").
to("xslt:${fee}").
to("direct:output");
foo - is a XML payload,
fee - XSLT template stored as java.lang.String,
output - xml payload
You can assign your variable into message header by some conditions:
.setHeader("TemplateLocation").constant("OSGI-INF/xsl/pretty.xsl")
After, you can use Recipient List EIP:
.recipientList().simple("xslt:${header.TemplateLocation}")
or you can use toD:
.toD("xslt:${header.TemplateLocation}")
Working example:
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.routeId("xsltTest")
.log(LoggingLevel.INFO, "XML input : \n${body}")
.setHeader("TemplateLocation").constant("OSGI-INF/xsl/pretty.xsl")
//.recipientList().simple("xslt:${header.TemplateLocation}")
.toD("xslt:${header.TemplateLocation}")
.to("log:end?level=INFO&showAll=true&multiline=true");
}
};
}
And there is no way to use a string variable as an xslt template, as far as I know.
Based on my knowledge
Your XSLT poller has a dynamic expression as subdirectory (${fee}).
As far as I know you cannot have a dynamic from address in a Camel
route. Because this would mean that you could consume from multiple
endpoints.
You can have it as separate file and call it like this
to("xslt:file:///foo/bar.xsl").
For more details XSLT
You cannot use a dynamic stylesheet (dynamic content) with the XSL component of Camel.
The most dynamic thing you can do is a dynamic reference to a static file like this:
.toD("xslt:${expressionWithFileReference}")
However, you can simply call a Java bean to do what you want and call it from the route:
.bean(javaBeanReference or new YourJavaBean())
In the Bean you can use Camel annotations to inject header(s), properties and the body into a method. Whatever you need from the current Camel Exchange.
public void yourMethod(
#Header(headername) String parameterName,
#Body Type parameterName) {
...
}
As Camel does not have support for dynamic XSLT input stream, I had to create my own Transformer. This might help someone
Here is my code snippet. I used camel processor as below
#Override
public void process(Exchange exchange) throws Exception {
XmlMapper xmlMapper = new XmlMapper();
Target target = xmlMapper.readValue(transform(getInputStreamFromDocument(xmlPayload), new ByteArrayInputStream(xsltTemplate.getBytes())), target.class);
}
public byte[] transform(InputStream dataXML, InputStream inputXSL)
throws TransformerException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource(inputXSL));
StreamSource in = new StreamSource(dataXML);
StreamResult out = new StreamResult(bos);
transformer.transform(in, out);
return bos.toByteArray();
}

javax.xml.bind.UnmarshalException, Unable to create an instance of class

I have the following situation:
There is a tree structure for logical expressions which is used in our application and defined by a four class hierarchie:
Node is an abstract super class
OrNode is a sub class of Node representing OR
AndNode is a sub class of Node representing AND
Leaf is a sub class of Node representing a leaf node holding some data
Now the tree structure should be transfered to a web service which will do some operation on the tree (e.g. evaluating the result by gathering some other information)
The signature of that WS-Operation could be look like the following:
public TheResult evaluateTree(Node tree);
We are using JAX-WS and generate the web services classes with wsimport. First, there are no classes generated for OrNode, AndNode and Leaf. So, we added them manually. We convert the classes used on the client side to the generated classes created by wsimport. Next we want to call the web service operation with the converted tree as parameter. But here an exception occurs saying something like:
javax.xml.ws.soap.SOAPFaultException: javax.xml.bind.UnmarshalException - with linked exception: [javax.xml.bind.UnmarshalException: Unable to create an instance of InterfaceEntities.Node - with linked exception: [java.lang.InstantiationException]]
Here are the Wrapper classes added by us and generated classes:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "OrNode")
public class OrNode
extends Node
{
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "AndNode")
public class AndNode
extends Node
{
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "leaf")
public class Leaf
extends Node
{
...
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "node", propOrder = {
"children",
"resultSet",
})
#XmlSeeAlso({
Leaf.class,
OrNode.class,
AndNode.class
})
public abstract class Node {
...
}
EDIT:
Here is the generated XML-File when using Marshaller as described in Blaise Doughan's blog (see answer below):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:treeInfo xmlns:ns2="http://webservice.api.process/">
<tree xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:OrNode">
<children xsi:type="ns2:leaf">
<!-- some fields holding values -->
</children>
<children xsi:type="ns2:leaf">
<!-- some fields holding values -->
</children>
</tree>
<!-- some fields holding values -->
</ns2:treeInfo>
It is a simple tree consisting of one orNode and two leaf nodes,
treeInfo represents the class holding the Node/tree object with some other information. It is marked as the XmlRootElement with the corresponding annotation.
Did we miss anything?
Thanks in advance!
I've found the problem. We are using several web services, for each we generate the wrapper classes via wsimport. And some webservices are using the node class. Now, as I mentioned in my question, we had to implement some wrapper classes manually as they were not auto generated by wsimport, i.e. we had to add wrapper classes for the OrNode and AndNode. You also have to add the XmlSeeAlso element to the super class so that it knows its sub classes. We added the XmlSeeAlso element for one web service but missed it for the other one. This caused the exception mentioned above.
If you are in development, a quick fix is to simply remove the abstract key word from the class definition of the "abstract" class. If the class is no longer abstract, it will no longer throw the error. This may be a hack that you want to fix before releasing your code into production, but it can enable you to keep developing other aspects of your application before you resolve the deeper problem.
Assuming in your question that your SearchNode class should really be Node then you need to make sure that the XML contains the appropriate xsi:type attribute to specify the subclass.
For the Leaf class it would be:
<node xsi:type="leaf" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
</node>
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html

How can I instruct a JAXB Marshaller to scrub base64Binary data only for auditing?

I am working in a JAXWS/JAXB web service environment. JAXWS out of the box uses uses the JAXB to marshal/unmarshaler the web service payloads.
I also have a requirement to audit all request and response payloads.
I want a compact and concise marshaled representation of the payload for the audit (as a irrelevant side note - I am auditing using a java.util.concurrent.BlockingQueue and some consumer threads to put batches of audit data in the audit datasource).
I have binary content(mtom) included on some web service response payloads but I DO NOT want to marshal audit these because the serialized base64 would be too large.
So my need is to create a marshaller (exclusively for auditng) that in all cases will scrub binary content but then NOT scrub for the prime purpose of marshalling web service response payloads. I do XSD to Java xjc. I need to use the same XSD/JAXB namespace for both contexts/marshallers.
Java type converter:
<jxb:javaType name=""
parseMethod="com.xxx.xxx.ws.converter.XXXLongConverter.parseXXXLong"
printMethod="com.xxx.xxx.ws.converter.XXXLongConverter.printXXXLong" />
is will not work because 1. I would need to unregister the adapter http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/Marshaller.html#setAdapter%28java.lang.Class,%20A%29
for the marshaller and I don't THINK I have a hook into that for JAXWS. 2. I can't be guaranteed the class name that JAXB will decide to create in order to unregister it.
I created my own XMLAdapter and used the annox jaxb plugin
but that didn't really work for the same reasons the above didn't work.
Update: I now tried manually and reflectively walking through payload(to be audited) prior to marshalling to scrub the binary data but that got to be too much pain for what it was worth.
I should also mention that for brevity of the audit I am using jersey JSON serialization supporting JAXB
but I don't think that takes away or adds to my base problem:
How can I scrub data in one marshaller/unmarshaller but not another but both whose origin is the same JAXB context?
UPDATE: Never figured out an elegate way to do this. Not really possible at this point with the frameworks as they are. UPDATE: Not true. Extending AttachmentMarshaller (I like this a lot and will use it) or creating a "need-aware" XmlAdapter would work for the audit specific marshaller as #Blaise answers below.
UPDATE: If I may take this a step further to round out my use case...I mentioned above that for brevity of the audit I'd like to do Json Serialization of the JSONJAXBContext using jersey apis, specifically using the JSONMarshaller but the interface does not define setAdapter and setAttachmentMarshaller. Coming out of JSONJAXBContext.createJSONMarshaller() is a JSONMarshallerImpl implementation which do define these this methods. I will grudgingly cast to impl so I can set my custom attachment marshaller.
How can I scrub data in one marshaller/unmarshaller but not another
but both whose origin is the same JAXB context?
You could set your own implementation of AttachemntMarshaller and set it on the Marshaller that you are using for auditing.
Root
Below is a sample domain object with a byte[] property that by default will be represented as an element of type base64Binary.
package forum8914008;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
byte[] bytes;
}
Demo
The demo code below first marshals the object to XML, and then marshals it a second time with a custom impplementation of AttachmentMarshaller set.
package forum8914008;
import javax.activation.DataHandler;
import javax.xml.bind.*;
import javax.xml.bind.attachment.AttachmentMarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.bytes = "Hello World".getBytes();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
marshaller.setAttachmentMarshaller(new AttachmentMarshaller() {
#Override
public boolean isXOPPackage() {
return true;
}
#Override
public String addMtomAttachment(DataHandler arg0, String arg1,
String arg2) {
return "fake";
}
#Override
public String addMtomAttachment(byte[] arg0, int arg1, int arg2,
String arg3, String arg4, String arg5) {
return "fake";
}
#Override
public String addSwaRefAttachment(DataHandler arg0) {
return "fake";
}
});
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code. The first XML document could grow to be quite large if the byte[] was big. The second XML document would stay the same size.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes>SGVsbG8gV29ybGQ=</bytes>
</root>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="fake"/>
</bytes>
</root>
How can I scrub data in one marshaller/unmarshaller but not another
but both whose origin is the same JAXB context?
You could support this use case with an XmlAdapter.
XmlAdapter (ByteArrayAdapter)
The following XmlAdapter is used to convert a byte[] to a byte[]. In its default state it will return the original byte[], it also has a audit state where it will return an empty byte[].
package forum8914008;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ByteArrayAdapter extends XmlAdapter<byte[], byte[]> {
private boolean audit;
public ByteArrayAdapter() {
this(false);
}
public ByteArrayAdapter(boolean audit) {
this.audit = audit;
}
#Override
public byte[] marshal(byte[] bytes) throws Exception {
if(audit) {
return new byte[0];
}
return bytes;
}
#Override
public byte[] unmarshal(byte[] bytes) throws Exception {
return bytes;
}
}
package-info
The #XmlJavaTypeAdapter annotation is used tp register the XmlAdapter. When used at the package level it will apply to all properties of the specified type in that package (see: http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html).
#XmlJavaTypeAdapter(value=ByteArrayAdapter.class, type=byte[].class)
package forum8914008;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
Root
Below is a sample domain object with a byte[] property that by default will be represented as an element of type base64Binary.
package forum8914008;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
byte[] bytes;
}
Demo
The demo code below first marshals the object with the default state of the ByteArrayAdapter which will return the real byte[] and the marshals the object a second time with a stateful ByteArrayAdapter set which will convert all byte[] values to an empty byte[].
package forum8914008;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.bytes = "Hello World".getBytes();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
marshaller.setAdapter(new ByteArrayAdapter(true));
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code. The XmlAdapter would apply to all mapped fields/properties of type byte[].
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes>SGVsbG8gV29ybGQ=</bytes>
</root>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes></bytes>
</root>

Passing serialized object through web service vs. Passing the object

I have a webservice in C#.NET with the following namespace:
[WebService (Namespace = "http://enterpriseName/wsName")]
The web service contains a WebMethod GetServiceObject and a class MyObject.
This web method returns a string whose content is a serialized instance of MyObject.
[WebMethod (MessageName = "GetServiceObjectXML" Description = "Get ServiceObject from Server to Client")]
public string GetServiceObjectXML ()
This method returns the following XML:
<? Xml version = "1.0" encoding = "utf-16"?>
<ServiceObject Xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<service>
<id>3</id>
<date>02/08/2010</date>
</service>
</Services>
</ServiceObject>
The problem that I encounter is that when I call this method from the client side and try to deserialize this xml to class MyObject and I get a NULL object.
After that I created a new WebMethod with the following signature:
[WebMethod (MessageName = "GetServiceObject" Description = "Get ServiceObject from Server to Client")]
public MyObject GetServiceObject ()
When I call this method from the client side I get the object filled correctly and I can also serialize the object without problems, but the result of serialization is the following xml:
<? Xml version = "1.0" encoding = "utf-16"?>
<ServiceObject Xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services Xmlns="http://enterpriseName/wsName">
<service>
<id>3</id>
<date>02/08/2010</date>
</service>
</Services>
</ServiceObject>
which is different from the xml generated by the WebMethod GetServiceObjectXML.
How can I get around this, since I intend to use both methods on the same webservice and in the same customer?
The obvious answer would be, fix GetServiceObjectXML() to return the same XML as GetServiceObject(). The difference seems to be that the object as serialized by the framework has a different XML namespace specified. Whatever method you're using to serialize the object into XML in GetServiceObjectXML() isn't doing that.

How to Declare Complex Nested C# Type for Web Service

I would like to create a service that accepts a complex nested type. In a sample asmx file I created:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class ServiceNest : System.Web.Services.WebService
{
public class Block
{
[XmlElement(IsNullable = false)]
public int number;
}
public class Cell
{
[XmlElement(IsNullable = false)]
public Block block;
}
public class Head
{
[XmlElement(IsNullable = false)]
public Cell cell;
}
public class Nest
{
public Head head;
}
[WebMethod]
public void TakeNest(Nest nest)
{
}
}
When I view the asmx file in IE the test page shows the example SOAP post request as:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<TakeNest xmlns="http://schemas.intellicorp.com/livecompare/">
<nest>
<head>
<cell>
<block xsi:nil="true" />
</cell>
</head>
</nest>
</TakeNest>
</soap:Body>
</soap:Envelope>
It hasn't expanded the <block> into its number member.
Looking at the WSDL, the types all look good. So is this just a limitation of the post demo page creator?
Thanks.
But those elements ARE null. You need to construct them before they show up otherwise they are just null.
As Kevin pointed out the example POST XML indicates that those elements are nil. I should have simply tried to consume the web service. Once I did that I could see that the importer (either .NET, Java or Ruby) correctly created all the types. So really there is no question here after all.
The .NET code did not give up after a certain number of levels.
If you look at the code generated by "Add Web Reference", you'll find that there's a bool numberSpecified field. Only if the client sets that to true will the number be serialized.
If you look at the XML Schema, you'll see that the number element might be absent. If it were of a reference type, then that could be represented in the client by a null value. Since it's an int, this additional flag is necessary to indicate whether or not to serialize this optional value.