Reputation: 211
I'm trying to manipulate data in a json-request-reply scenario for a REST-API.
To simplify my problem, let's say I want to replace all 'b' characters with a 'd' inside the payload.
Is there a way for me to work natively on the json-data, and not convert the data to XML first?
I'm trying to build a custom class mediator to put in my OutSequence, but since I only have access to the MessageContext, which treats the payload as XML, I'm running into problems.
The problem is that the json cannot be transformed to and from XML.
It has this part inside its structure:
"Destination": {
"name": "abc",
"type": "ST",
"$": "\n"
}
The "$" property is valid json, but since WSO2 ESB always handle data as XML inside its MessageContext, it can't transform that property to tags obviously, so whenever I do
MessageContext.getEnvelope().getBody()
inside my class mediator, the response is:
<Destination>
<name>abc</name>
<type>ST</type>
</Destination>
Missing the $ property.
I'm using the Message builder: org.apache.synapse.commons.json.JsonStreamBuilder and formatter: org.apache.synapse.commons.json.JsonStreamFormatter
To pass through the content in normal cases (otherwise it would fail in the XML to JSON processing step). But there must be a way for me to manipulate the data as native JSON (or as a native String?) perhaps being able to get hold of the InputStream and manipulate the data from that? But I can't find a way to get to the InputStream from the Message Context?
Upvotes: 0
Views: 1413
Reputation: 211
Turns out that WSO2 themselves has written logic to handle $-characters.
From their support-document (https://docs.wso2.com/display/ESB481/JSON+Support):
When building XML elements, the ESB handles the ‘$’ character and digits in a special manner when they appear as the first character of a JSON key.
The $-character is replaced in the XML representation as "_JsonReader_PS_". However, I looked into the code for this (love open source), and this is the code handling this case:
// "$a" replace with "_JsonReader_PS_a"
private String replaceInvalidCharacters(String keyString){
if(Pattern.compile("^[0-9]").matcher(keyString).find()) {
return "_JsonReader_PD_" + keyString;
} else if (keyString.startsWith("$")){
return "_JsonReader_PS_" + keyString.substring(1);
} else
return keyString;
}
As we can see, the code assumes the property will begin with a $-character and have at least one other character. This is not true in our case, which is why the code fails for us and the property is "lost".
In order to circumvent this, I wrote a custom JSONStreamBuilder to replace all $-characters in the actual InputStream, to make sure that the conversation was handled correctly.
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.axiom.om.OMElement;
import org.apache.axis2.AxisFault;
import org.apache.axis2.builder.Builder;
import org.apache.axis2.context.MessageContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.commons.json.JsonStreamBuilder;
/*
* As WSO2 JSON parser can't handle single $ properties when converting between JSON and XML
* This custom JSON Stream builder replaces all $ signs with the constant '_JsonReader_PS_',
* which the WSO2 parser treats as a $ sign when transforming.
*/
public class CustomJsonStreamBuilder implements Builder {
private static final Log logger = LogFactory.getLog(CustomJsonStreamBuilder.class.getName());
JsonStreamBuilder jsonStreamBuilder = new JsonStreamBuilder();
public OMElement processDocument(InputStream arg0, String arg1, MessageContext arg2) throws AxisFault {
logger.debug("Processing input stream for custom JSON stream builder");
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String line;
try {
br = new BufferedReader(new InputStreamReader(arg0));
while ((line = br.readLine()) != null) {
sb.append(line);
}
String input = sb.toString();
/*
* WSO2 JSON parser treats _JsonReader_PS_ as $ in JSON structure
*/
input = input.replace("$", "_JsonReader_PS_");
InputStream is = new ByteArrayInputStream(input.getBytes("utf-8"));
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return jsonStreamBuilder.processDocument(is, arg1, arg2);
} catch (Exception e) {
e.printStackTrace();
logger.error("Problem processing input stream for custom JSON stream builder", e);
}
return jsonStreamBuilder.processDocument(arg0, arg1, arg2);
}
}
After I replaced the JSON stream builder in the axis2 configuration I could happily do any scripting techniques on the payload in my sequences and keep the $-character property.
I hope this helps anyone facing the same issue as me.
Upvotes: 1