Skip to Content

When you came across suggesting a solution for converting an Incoming XML to JSON, you would directly convert it using our standard REST Adapter. which will do our work.

 

But I have faced a problem which is well mentioned in  blog-:

  • “If an XML element was defined as an array, but only contains one item in converted XML payload, Jettison processor will likely convert it to a non-array type;”

As , It has been handled by the Enhanced Version of the Rest Adapter, which has solved the above mentioned issue that we face.(After SP12)

 

But what if you don’t have the current patch available in your PI system, how will you handle it?

So, this blog is all about how to handle the issue, if you dont have the option for Enhanced Conversion in REST Adapter.

I have faced this issue recently, I have already suggested for a patch upgrade, but It was not a feasible solution, at that point of time. So, the XML has to be changed to JSON manually.

 

The first thing that came into my mind, was to take help of Java Mapping, which could do my task. After a certain amount of research, I thought of handling this issue, with Jackson JSON Java Parser.

 

I have gone through lot of blogs having different styles of Java mapping, which was handling the issue. But none of them were handling my issue.

 

Below is the sample XML that I had.

<?xml version=”1.0″ encoding=”UTF-8″?>
<n0:namespace>
            <Header>
                  <A>Value<A>
                  <B>Value</B>
                  <C>Value</C>
                  <D>2018-08-07T10:57:04Z</D>
            </Header>
            <A1>
                   <A2>
                            <A12>Value</A12>
                             <B>
                                    <B1>Value</B1>
                                    <B2>Value</B2>
                                    <B3>Value</B3>
                              </B>

                              <B>
                                    <B1>Value</B1>
                                    <B2>Value</B2>
                                    <B3>Value</B3>
                              </B>              

                   </A2>
          </A1>

           <A1>
                   <A2>
                            <A12>Value</A12>
                             <B>
                                    <B1>Value</B1>
                                    <B2>Value</B2>
                                    <B3>Value</B3>
                              </B>

</namespace>

 

So, using the standard REST Adapter, I was getting the usual issue, that if a single object was encountered (here in the above XML is the second <A1> object contains single <B>), so we had non array values for that.

I took help of 3 Jackson Parser which are-

  1. jackson-annotations
  2. jackson-core
  3. jackson-databind

 

This 3 external Jars will be used in the java mapping apart from your own PI mapping jar file.

So please find the below code that has been used-:

 

The Main class-:

package com.conversion;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

import javax.xml.bind.JAXB;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.sap.aii.mapping.api.AbstractTransformation;
import com.sap.aii.mapping.api.StreamTransformationException;
import com.sap.aii.mapping.api.TransformationInput;
import com.sap.aii.mapping.api.TransformationOutput;

public class XMLtoJSON extends AbstractTransformation{

@Override
public void transform(TransformationInput arg0, TransformationOutput arg1) throws StreamTransformationException {
try {
execute(arg0.getInputPayload().getInputStream(), arg1.getOutputPayload().getOutputStream());
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public void execute(InputStream arg0, OutputStream arg1) throws IOException, JsonGenerationException, JsonMappingException {
ParentTag catalog = JAXB.unmarshal(arg0, ParentTag.class);  //unmarshaling The XML
ObjectMapper mapper = new ObjectMapper();                    //converting into JSON
mapper.setPropertyNamingStrategy(new MyPropertyNamingStrategy());
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.writeValue(arg1, catalog);
//arg1.write(catalog.toString().getBytes());
}

public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
InputStream in = new FileInputStream(new File(“D:/json_Test/inputxml.xml”));
OutputStream out = new FileOutputStream(new File(“D:/json_Test/output.xml”));
XMLtoJSON xmLtoJSON = new XMLtoJSON();
xmLtoJSON.execute(in, out);
}

}

 

Child classes-:

 

For the Tags I have created getter and setter class,

(I will not be listing out all the classes for the Parent nodes, just a overview.)

 

package com.conversion;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@XmlAccessorType(XmlAccessType.FIELD)
@JsonPropertyOrder({“Header”,”A”})
public class ParentTag {

private Header Header;

private A A;

public Header getHeader() {
return Header;
}

public void setHeader(Header header) {
this.Header = header;
}

@XmlElement(nillable=false)
public A getA() {
return A;
}

public void setA(A a) {
this.A = a;
}

}

 

Getter Setter for class A1

 

package com.conversion;

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

@XmlAccessorType(XmlAccessType.FIELD)
public class A1 {

private List<A2> A2;

public List<A2> getA2() {
return A2;
}

public void setA2(List<A2> a2) {
A2 = a2;
}
}

Similarly we can do for the other Parent classes.

 

This class maintains the structure of the JSON output, which will pick the same name tag from the XML and will be returned in output.

package com.conversion;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;

public class MyPropertyNamingStrategy extends PropertyNamingStrategy {
/**
*
*/
private static final long serialVersionUID = 1L;

@Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
return convert(field.getName());
}

@Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
return convert(method.getName().toString());
}

@Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
return convert(method.getName().toString());
}

private String convert(String input) {
return input.substring(3);
}
}

 

This how I have handled the issue. There will be lot of other ways, how you can handle this,But this is how I have handled this situation till the next patch is available.

Hopefully this piece of code help those, who have faced the similar situation like me.

 

Please do let me know, if you have handled this situation in any other optimized method!!

 

 

 

To report this post you need to login first.

3 Comments

You must be Logged on to comment or reply to a post.

    1. Varinder singh

      Hi Gabriel,

       

      The mentioned xslt doesn’t recognize source XSD. If any node is of array type but only one value is coming in xml, this xslt will treat it as non array object for the conversion.

       

      Br,

      Varinder

      (0) 

Leave a Reply