I have a larga data JSON which I want to pass to the backend to be parsed there to become into java objects.
To make this I'm using the JSON.stringify function but inside the JSON there is an array attribute that the JSON.stringify is enclosing between quotes ("), so when Gson find it (the way I'm using at the backend to decode the string into objects), it throws an error because this is not an array inside a JSON string representation, but an string attibute inside a JSON string representation.
This is an example of the string generated with JSON.stringify:
{"id":0, "array": "[{\"id\":0, \"cod\": \"XXX\"}, {\"id\":0, \"cod\": \"XXX\"}]"}
The array attribute cannot be converted by Gson because is not an array.
Can anybody help me with this issue?
Thanks a lot.
I'd likely prefer to fix the generated JSON, but if that's not possible or otherwise preferable, it looks like you'll simply need to deserialize part of the JSON twice. This could be accomplished with a custom deserializer as follows.
import java.lang.reflect.Type;
import java.util.Arrays;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
public class GsonFoo
{
public static void main(String[] args)
{
// With the generated JSON corrected:
// {"id":42, "array": [{"id":1, "cod": "aaa"}, {"id":2, "cod": "bbb"}]}
String jsonInput = "{\"id\":42, \"array\": [{\"id\":1, \"cod\": \"aaa\"}, {\"id\":2, \"cod\": \"bbb\"}]}";
Gson gson = new Gson();
Bar bar1 = gson.fromJson(jsonInput, Bar.class);
System.out.println(bar1);
// Bar: id=42, array=[Thing: id=1, cod=aaa, Thing: id=2, cod=bbb]
// -------------------------
// With the funky JSON:
// {"id":42, "array": "[{\"id\":1, \"cod\": \"aaa\"}, {\"id\":2, \"cod\": \"bbb\"}]"}
String funkyJsonInput = "{\"id\":42, \"array\": \"[{\\\"id\\\":1, \\\"cod\\\": \\\"aaa\\\"}, {\\\"id\\\":2, \\\"cod\\\": \\\"bbb\\\"}]\"}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Thing[].class, new FunkyThingArrayDeserializer());
gson = gsonBuilder.create();
Bar bar2 = gson.fromJson(funkyJsonInput, Bar.class);
System.out.println(bar2);
// Bar: id=42, array=[Thing: id=1, cod=aaa, Thing: id=2, cod=bbb]
}
}
class FunkyThingArrayDeserializer implements JsonDeserializer<Thing[]>
{
#Override
public Thing[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException
{
String actualJson = context.deserialize(json, String.class);
return new Gson().fromJson(actualJson, Thing[].class);
}
}
class Bar
{
int id;
Thing[] array;
#Override
public String toString()
{
return String.format("Bar: id=%d, array=%s", id, Arrays.toString(array));
}
}
class Thing
{
int id;
String cod;
#Override
public String toString()
{
return String.format("Thing: id=%d, cod=%s", id, cod);
}
}
You need to write this code before invoke JSON.stringify
if(window.Prototype) {
delete Object.prototype.toJSON;
delete Array.prototype.toJSON;
delete Hash.prototype.toJSON;
delete String.prototype.toJSON;
}
Related
I have this situation:
More servlets set the httpservletResponse content-type to json/application.
I output my json this way:
out.write (new Gson().toJson(myObject));
where myObject, is an object and the code above provides creating a string json like using myObject structure.
Now I need to add an argument on the top of the json, because I need an argument like: "result":"okay".
Is there a way to add this without changinc myObject class?
Thanks in advance.
Yes. Instead of building a String using Gson#toJson() use Gson#toJsonTree() which parses the object but creates a JSON internal representation using JsonElement subclasses. You will need to cast it to a JsonObject, then add the new property and finally write it to the output stream.
Code example:
package net.sargue.gson;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class SO36837061 {
public static class MyClass {
int a = 1;
String b = "salut";
}
public static void main(String[] args) {
MyClass myObject = new MyClass();
JsonObject jsonObject = (JsonObject) new Gson().toJsonTree(myObject);
jsonObject.addProperty("result", "okay");
String json = new Gson().toJson(jsonObject);
System.out.println("json = " + json);
}
}
Output:
json = {"a":1,"b":"salut","result":"okay"}
I understand the purpose of the #SerializedName annotation. This is used while converting the java object to Json to override the default name of the member. Now I have a scenario where the incoming Json has an element with the key "318".Obviously I cannot have a Java member name starting with a numeric. How will I map this Json element "318" to a java member with the name something like "threeOneEight". Any idea?
You use the exact same method you used the other way around: #SerializedName.
Check this:
package net.sargue.gson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import org.intellij.lang.annotations.Language;
public class SO36488332 {
public static void main(String[] args) {
#Language("JSON")
String json = "{\n" +
" \"318\": \"I am a String.\"\n" +
"}";
Gson gson = new GsonBuilder().create();
MyPOJO myPOJO = gson.fromJson(json, MyPOJO.class);
System.out.println("myPOJO.threeOneEight = " + myPOJO.threeOneEight);
}
private static class MyPOJO {
#SerializedName("318")
private String threeOneEight;
}
}
The execution result is:
myPOJO.threeOneEight = I am a String.
SO I am making a rest request to the JIRA API and getting a json response that includes all objects.
my request look like this:
Set restReq = CreateObject("MSXML2.ServerXMLHTTP.3.0")
restReq.open "GET", "URI",False
restReq.setRequestHeader "Authorization","Basic{user:Password}"
restReq.setOption SXH_OPTION_IGNORE_SERVER_SSL_CERT_ERROR_FLAGS,SXH_SERVER_CERT_IGNORE_ALL_SERVER_ERRORS
restReq.send("")
'response.write(restReq.responseText)
the response.write looks like this (but much longer):
[{"self":"https://JIRA:8343/rest/api/2/project/CT","id":"10004","key":"CT","name":"Core Technologies"}},
{"self":"https://JIRA:8343/rest/api/2/project/CTCCG","id":"10006","key":"CTCCG","name":"CT CCG"}}]
I would like to be able to loop through the response and use the "id", "key" and "name" in an unordered list. I can create a ul, but how do I extract the information I need from the json?
You check this question relating to using the Gson library. It is very small, quick and easy to use to convert between JSON to Objects.
import java.io.FileReader;
import com.google.gson.Gson;
public class Test {
public static void main(String[] args) throws Exception
{
Gson gson = new Gson();
TypeDTO[] myTypes = gson.fromJson(new FileReader("D:\\temp\\inputjson.txt"), TypeDTO[].class);
for (int i = 0; i < myTypes.length; ++i)
System.out.println(myTypes[i].self);
}
class TypeDTO
{
String self;
String id;
String key;
String name;
}
}
inputjson.txt had
[{"self":"https://JIRA:8343/rest/api/2/project/CT","id":"10004","key":"CT","name":"Core Technologies"},
{"self":"https://JIRA:8343/rest/api/2/project/CTCCG","id":"10006","key":"CTCCG","name":"CT CCG"}]
note the absence of addtional } when compared to yours at the end of each line.
First, I have a very simple java bean which can be easily serialized to json:
class Node {
private String text;
// getter and setter
}
Node node = new Node();
node.setText("Hello");
String json = new Gson().toJson(node);
// json is { text: "Hello" }
Then in order to make such beans have some dynamic values, so I create a "WithData" base class:
Class WithData {
private Map<String, Object> map = new HashMap<String, Object>();
public void setData(String key, Object value) { map.put(key, value); }
public Object getData(String key) = { return map.get(key); }
}
class Node extends WithData {
private String text;
// getter and setter
}
Now I can set more data to a node:
Node node = new Node();
node.setText("Hello");
node.setData("to", "The world");
But Gson will ignore the "to", the result is still { text: "Hello" }. I expect it to be: { text: "Hello", to: "The world" }
Is there any way to write a serializer for type WithData, that all classes extend it will not only generate its own properties to json, but also the data in the map?
I tried to implement a custom serializer, but failed, because I don't know how to let Gson serialize the properties first, then the data in map.
What I do now is creating a custom serializer:
public static class NodeSerializer implements JsonSerializer<Node> {
public JsonElement serialize(Node src,
Type typeOfSrc, JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.addProperty("id", src.id);
obj.addProperty("text", src.text);
obj.addProperty("leaf", src.leaf);
obj.addProperty("level", src.level);
obj.addProperty("parentId", src.parentId);
obj.addProperty("order", src.order);
Set<String> keys = src.getDataKeys();
if (keys != null) {
for (String key : keys) {
obj.add(key, context.serialize(src.getData(key)));
}
}
return obj;
};
}
Then use GsonBuilder to convert it:
Gson gson = new GsonBuilder().
registerTypeAdapter(Node.class, new NodeSerializer()).create();
Tree tree = new Tree();
tree.addNode(node1);
tree.addNode(node2);
gson.toJson(tree);
Then the nodes in the tree will be converted as I expected. The only boring thing is that I need to create a special Gson each time.
Actually, you should expect Node:WithData to serialize as
{
"text": "Hello",
"map": {
"to": "the world"
}
}
(that's with "pretty print" turned on)
I was able to get that serialization when I tried your example. Here is my exact code
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;
public class Class1 {
public static void main(String[] args) throws MalformedURLException {
GsonBuilder gb = new GsonBuilder();
Gson g = gb.setPrettyPrinting().create();
Node n = new Node();
n.setText("Hello");
n.setData("to", "the world");
System.out.println(g.toJson(n));
}
private static class WithData {
private Map<String, Object> map = new HashMap<String, Object>();
public void setData(String key, Object value) { map.put(key, value); }
public Object getData(String key) { return map.get(key); }
}
private static class Node extends WithData {
private String text;
public Node() { }
public String getText() {return text;}
public void setText(String text) {this.text = text;}
}
}
I was using the JDK (javac) to compile - that is important because other compilers (those included with some IDEs) may remove the information on which Gson relies as part of their optimization or obfuscation process.
Here are the compilation and execution commands I used:
"C:\Program Files\Java\jdk1.6.0_24\bin\javac.exe" -classpath gson-2.0.jar Class1.java
"C:\Program Files\Java\jdk1.6.0_24\bin\java.exe" -classpath .;gson-2.0.jar Class1
For the purposes of this test, I put the Gson jar file in the same folder as the test class file.
Note that I'm using Gson 2.0; 1.x may behave differently.
Your JDK may be installed in a different location than mine, so if you use those commands, be sure to adjust the path to your JDK as appropriate.
I am having a deserialization problem using the GSON library.
The following is the JSON code which I try to deserialize
{"response": {
"#service": "CreateUser",
"#response-code": "100",
"#timestamp": "2010-11-27T15:52:43-08:00",
"#version": "1.0",
"error-message": "",
"responseData": {
"user-guid": "023804207971199"
}
}}
I create the following classes
public class GsonContainer {
private GsonResponse mResponse;
public GsonContainer() { }
//get & set methods
}
public class GsonResponse {
private String mService;
private String mResponseCode;
private String mTimeStamp;
private String mVersion;
private String mErrorMessage;
private GsonResponseCreateUser mResponseData;
public GsonResponse(){
}
//gets and sets method
}
public class GsonResponseCreateUser {
private String mUserGuid;
public GsonResponseCreateUser(){
}
//get and set methods
}
After calling the GSON library the data is null. Any ideas what is wrong with the classes?
Thx in advance for your help ... I assume it's something trivial ....
#user523392 said:
the member variables have to match exactly what is given in the JSON response
This is not the case.
There are a few options for specifying how Java field names map to JSON element names.
One solution that would work for the case in the original question above is to annotate the Java class members with the #SerializedName to very explicitly declare what JSON element name it maps to.
// output: [MyObject: element=value1, elementTwo=value2]
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
public class Foo
{
static String jsonInput =
"{" +
"\"element\":\"value1\"," +
"\"#element-two\":\"value2\"" +
"}";
public static void main(String[] args)
{
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
MyObject object = gson.fromJson(jsonInput, MyObject.class);
System.out.println(object);
}
}
class MyObject
{
String element;
#SerializedName("#element-two")
String elementTwo;
#Override
public String toString()
{
return String.format(
"[MyObject: element=%s, elementTwo=%s]",
element, elementTwo);
}
}
Another approach is to create a custom FieldNamingStrategy to specify how Java member names are translated to JSON element names. This example would apply the same name mapping to all Java member names. This approach would not work for the original example above, because not all of the JSON element names follow the same naming pattern -- they don't all start with '#' and some use camel case naming instead of separating name parts with '-'. An instance of this FieldNamingStrategy would be used when building the Gson instance (gsonBuilder.setFieldNamingStrategy(new MyFieldNamingStrategy());).
class MyFieldNamingStrategy implements FieldNamingStrategy
{
// Translates the field name into its JSON field name representation.
#Override
public String translateName(Field field)
{
String name = field.getName();
StringBuilder translation = new StringBuilder();
translation.append('#');
for (int i = 0, length = name.length(); i < length; i++)
{
char c = name.charAt(i);
if (Character.isUpperCase(c))
{
translation.append('-');
c = Character.toLowerCase(c);
}
translation.append(c);
}
return translation.toString();
}
}
Another approach to manage how Java field names map to JSON element names is to specify a FieldNamingPolicy when building the Gson instance, e.g., gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);. This also would not work with the original example, however, since it applies the same name mapping policy to all situations.
The JSON response above cannot be deserialized by GSON because of the special characters # and -. GSON is based on reflections and the member variables have to match exactly what is given in the JSON response.