Parsing JSON String as simple as possible with GSON - java

Using GSON, how can i return a single key from a Multidimensional Json String?
Here is the Multidimensional Json String:
{"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
I want to return the "session_token" key value.
I'm trying this:
class app {
static class Response {
String session_token;
}
public void getSessionToken() {
String x = {"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
Response r = new Gson().fromJson(x, Response.class);
System.out.println(r.session_token);
}
}
But with this, my r.session_token returns null.

You would need to use Gson's JsonParser class directly and extract the data from the parse tree:
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(myJsonString);
JsonObject jsonObject = element.getAsJsonObject();
String lastName = jsonObject.get("lastname").getAsString();
System.out.println(lastName);
That said, it's debatable whether this would save you any real time over:
(edited from comments below):
class App {
static class Response {
String lastname;
}
public static void main(String[] args) {
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
Response r = new Gson().fromJson(myJsonString, Response.class);
System.out.println(r.lastname);
}
}
Gson will silently ignore the fact that there's more data in the JSON than you're interested in, and later on you might be interested in it, in which case it's trivial to add fields to your Response class.
Edit due to question changing:
You have a JSON object. It contains a field data whose value is an object. Inside that object you have a field session_token that you're interested in.
Either you have to navigate to that field through the parse tree, or you have to create Java classes that all will map to. The Java classes would resemble (at the bare minimum):
class Response {
Data data;
}
class Data {
String session_token;
}

Related

How to serialize an object and read it into JsonNode?

I have a simple Java object that has a String property that contains serialized java objects:
#Data
private class Test {
int id;
String data;
}
The data is created with
test.setData(mapper.writeValueAsString(anyJavaObjectThatShouldBeSerialized));
Then I send the Test Object to another server, where I want to deserialize the object from the data property into a JSONNode:
JsonNode node = mapper.readTree(test.getData());
The problem is, that the result of readTree is a TextNode and not a JsonNode. Therefore I can not access the properties of the serialized data.
The problem might be that the serialized object is interpreted as a String because when I print it it has quotationsmarks around it:
"{\"id\":39,\"name\":\"Im a test value\"}"
How do I get a JsonNode from the String that contains serialized objects? Please note that the data can be ANY Java object, that's why I serialized it into a String.
Make a double deserialization - first deserialize the string payload to json, then deserialize the json to class, or JsonNode in your case.
public class Temp {
public static void main(String[] args) throws Exception {
String payload = //your string payload;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.readValue(payload, String.class);
JsonNode node = mapper.readTree(json);
}
}
As you noted, the cause is that you are making double serialization - first when setting the data field, then again when sending data to the other server.
You can avoid this double serialization/deserialization by making data an object.
#Data
class Test {
int id;
Object data;
}
test.setData(anyJavaObjectThatShouldBeSerialized);
Like this it will be serialized like JsonObject.
{
"id": 39,
"name": "Im a test value"
}

Parse a JSON structure with symbol instead of a string for an attribute

I am working on an HTTP Rest query.
Wikipedia API returns a JSON. The problem is the JSON structure returned by Wikipedia.
https://en.wikipedia.org/w/api.php?action=parse&section=0&prop=text&format=json&page=pizza
This is the JSON obtained via a rest request to the Wikipedia API. To view the full JSON, you can click on the above link.
{
"parse":
{
"title":"Pizza",
"pageid":24768,
"text":{"*":"<div class=\...>"}
}
}
I would parse this using a custom deserializer which I haven't got a chance to test. Trying to parse with simple Gson like the following return null;
Result res = new Gson(str,Result.class);
I have created the classes like the following:
public class Result
{
private Parse parse;
}
public class Parse
{
private String title;
private int pageid;
private Text text;
}
public class Text{
private String *;// what should I call this attribute.
}
My plan is to add a custom deserializer like the following:
public class TextBaseDeserializer implements JsonDeserializer<Text> {
#Override
public RespondentBase deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return jsonDeserializationContext.deserialize(jsonElement.get("*"),Text.class);
}
}
I am using Gson to parse this, like the following:
Gson tmp = new GsonBuilder().registerTypeAdapter(Text.class, new TextBaseDeserializer());
// let's assume that str is the string obtained following Rest based request from Java.
Result res = tmp.parse(str,Result.class);
I have done all the above code to handle the symbolic JSON attribute.
My question is how to parse such a JSON. In the above example, the attribute is a *
As mentioned in a comment, you should annotated field with #SerializedName("*").
You can name the field whatever you want. We'll just name it star below, but maybe all is better, since * might be a wildchar? Doesn't really matter, just choose whatever name you like.
class Text
{
#SerializedName("*")
private String star;
}
Test
String str = "{\"parse\":{\"title\":\"Pizza\",\"pageid\":24768,\"text\":{\"*\":\"<div class=\\\"mw-parser-output\\\">...</div>\"}}}";
Gson tmp = new GsonBuilder().create();
Result res = tmp.fromJson(str, Result.class);
System.out.println(res.getParse().getText().getStar());
Output
<div class="mw-parser-output">...</div>

Spring Boot REST application with Jackson - how to handle arbitrary/unknown xml structures passed in?

I am working with a Spring Boot REST application. We are using jackson to handle deserialization of XML as well as JSON passed in the request body. An example of an expected request body looks like this:
<formInput><formNum>999999</formNum><documentData>Completely unknown data structure here!</documentData></formInput>
In the documentData element, we will have a structure that is completely arbitrary/unknown on the server side. We don't care about the structure, because we only want to pass the xml that is nested in documentData on to another service.
The POJO that we are trying to map the request body onto looks like this:
#JsonDeserialize(using=FormInputJsonDeserializer.class)
public class FormInput {
private String formNum
private String documentData
public String getFormNum() {
return formNum
}
public void setFormNum(String formNum) {
this.formNum = formNum
}
public String getDocumentData() {
return documentData;
}
public void setDocumentData(String documentData) {
this.documentData = documentData;
}
}
The custom JsonDeserializer that we are trying to write:
public class FormInputJsonDeserializer extends JsonDeserializer<FormInput> {
#Override
public FormInput deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
FormInput formInput = new FormInput();
String fieldName
JsonToken currentToken
while (parser.nextToken() != null) {
currentToken = parser.getCurrentToken()
if (currentToken.equals(JsonToken.END_OBJECT)) {
continue
}
fieldName = parser.getCurrentName()
// formNum handling not written yet
if ("documentData".equalsIgnoreCase(fieldName)) {
if (parser.getCurrentToken().equals(JsonToken.START_OBJECT)) {
// we are at the start of documentData, and we need to capture the
// entire documentData node as a String since we don't know
// its structure
JsonFactory jfactory = new JsonFactory()
StringWriter jsonStringWriter = new StringWriter()
JsonGenerator jGen = jfactory.createGenerator(jsonStringWriter)
jGen.copyCurrentStructure(parser) // points to END_OBJECT after copy
jGen.close()
String documentDataJsonStr = jsonStringWriter.getBuffer().toString()
println("documentDataJsonStr: " + documentDataJsonStr)
}
}
}
// rest of code omitted
}
}
As I say, if the request body is xml, ideally I'd like to just keep it formatted as xml and assign that to the documentData String property. However I came up with the above custom deserialization code by following some other examples on StackOverflow. This parsing code ends up converting documentData to a JSON formatted String. Since I didn't know how to pass through the raw XML and get it mapped to the String property, I thought I could just convert the JSON formatted String back to a XML formatted String. A problem arises when we pass in a XML structure like this:
<formInput><formNum>9322</formNum><documentData><repeatLevel><subForm1><GROSS_DISTR> 13,004.31</GROSS_DISTR><GROSS_DISTR> 13,004.31</GROSS_DISTR><GROSS_DISTR> 13,004.31</GROSS_DISTR></subForm1></repeatLevel><repeatLevel><subForm1><GROSS_DISTR> 38,681.37</GROSS_DISTR><GROSS_DISTR> 38,681.37</GROSS_DISTR><GROSS_DISTR> 38,681.37</GROSS_DISTR></subForm1></repeatLevel></documentData></formInput>
After documentData is parsed in the deserialize method, the println statement shows the parsed JSON String as:
{"repeatLevel":{"subForm1":{"GROSS_DISTR":" 13,004.31","GROSS_DISTR":" 13,004.31","GROSS_DISTR":" 13,004.31"}},"repeatLevel":{"subForm1":{"GROSS_DISTR":" 38,681.37","GROSS_DISTR":" 38,681.37","GROSS_DISTR":" 38,681.37"}}}
This is actually not strictly valid JSON, due to the duplicate keys. I would have hoped that these would have been converted to JSON arrays, but that is not the case. So, I am unable to turn around and use something like the JSON.org libraries (JsonObject and XML) to convert the JSON String back to XML format (get an exception with a "duplicate key" error).
Does anybody have any suggestions or strategies for handling our situation?
You could try to use a JSONObject, add the #JsonIgnoreProperties("documentData") tag and extract documentData seperately from the raw data using substring()

Is there a way to read a string into the Jackson API to easily get back a JSON object

Is there was a way to pass a String into some Jackson object and have it populate the JSON obj for me? Maybe I'm comparing apples to oranges but the json-rpc-1.0.jar library allows me to do this:
// string will be read in from file but putting the string below just to show what i'm trying to do.
JSONObject jsonObj;
String testStr = "{"blah":123, "aaa": "got here", "foo":"bar", "bar":123}";
jsonObj = new JSONObject(testStr);
jsonObj.put("blah",345);
If I execute
System.out.println(jsonObj);
I get:
{"blah":345, "aaa": "got here", "foo":"bar", "bar":123}
The problem with the json-rpc-1.0.jar file is it doesn't play nicely with long primitive types. For some reason, it converts long data to something like 1.32e9 if I tried to assign a timestamp (long data type) to a field.
I found Jackson (jackson-core-2.2.3.jar) is nicer to longs, preserving the 10-13 digits I need for my timestamp. However, I can't find anything that works like the above snippet of code in Jackson. The closest might be ObjectMapper.readValue but it's not exactly like above.
Please let me know if this is possible or if I'm just dreaming. Thanks in advance for your help. In the meantime, I will try to look at the API some more.
IMO this is not how Jackson is meant to be used. With Jackson, an object should be serialized with the fields of its class. You shouldn't be adding anything to that JSON afterwards. For the sake of the question, however, here's what you can do. Take for example
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
MyClass a = new MyClass();
ObjectNode node = mapper.<ObjectNode>valueToTree(a);
node.put("blah", "123");
System.out.println(node);
}
static class MyClass {
private String value = "some text";
private long timestamp = System.currentTimeMillis();
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
which prints
{"value":"some text","timestamp":1384233053765,"blah":"123"}
The valueToTree() method will convert your object into an ObjectNode which is kind of a tree that holds the various JSON elements. You can modify this ObjectNode by adding or removing elements. That is what we do with node.put("blah", "123");. It will add a Json object with name blah and value "123".

Object constructor from json or xml string

I'm working on a service that spits json everywhere. However one of the providers I consume uses XML as serialization format so I want to be able to use the same interface for parsing (and spitting out) JSON with that XML.
Problem is, I don't know of a XML type, or object that would allow me to override my constructor easily.
Hoping to clarify my point, here's some code:
public class JsonData {
private Hashtable<String, Variant> map = new Hashtable<String, Variant>();
public JsonData() {
}
public JsonData(String jsonString) {
this.deserialize(jsonString);
}
Ideally I would like a third constructor to do something like:
public JsonData(XMLString jsonString) {
this.xmldeserialize(jsonString);
}
Note how both relevant constructors take a plain string as argument.
Any pointer?
You can use static methods to create object from json or xml strings:
public static JsonData fromJson(String json) {
JsonData data = new JsonData();
data.deserializeJson(json);
return data;
}
public static JsonData fromXml(String xml) {
JsonData data = new JsonData();
data.deserializeXml(xml);
return data;
}
Can't you just check if the input is json or xml (using regex) and call appropriate method to deserialize.
Like
public JsonData(String jsonString) {
if(isValidJson(jsonString){
this.deserialize(jsonString);
} else {
this.xmldeserialize(jsonString);
}
}

Categories