How should I deserialize following JSON to skip root element and parse just the inner part of this JSON. I'd like to avoid creating additional, 3rd class Root, which would include only MapWrapper field.
{
"root": {
"language": "en",
"map": {
"k1": {
"name": "n1",
},
"k2": {
"name": "n2",
}
}
}
}
So I'd like to have only these two classes:
class MapWrapper {
private String language;
private Map<String, MyMapEntry> map;
}
class MyMapEntry {
String name;
}
you can use GSON Library for this.
Below code will solve your problem.
public class ConvertJsonToObject {
private static Gson gson = new GsonBuilder().create();
public static final <T> T getFromJSON(String json, Class<T> clazz) {
return gson.fromJson(json, clazz);
}
public static final <T> String toJSON(T clazz) {
return gson.toJson(clazz);
}
}
String json; // your jsonString
Map<String,Object> r = ConvertJsonToObject.getFromJSON(json,Map.class);
String innerJson = ConvertJsonToObject.toJson(r.get("root"));
MapWrapper _r = ConvertJsonToObject.getFromJSON(innerJson,MapWrapper.class);
Consider the following JSON:
{"authorization":{"username":"userabc", "password":"passabc"}}
The DTO for this JSON without the root element
public class Authorization {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
// Add a container for the root element
public static class Container {
public Authorization authorization;
}
}
Convert from/to JSON using the following methods (you can either keep this within DTO or some other help class)
public String toJson(Authorization authorization) {
Gson gson = new Gson();
Authorization.Container container = new Authorization.Container();
container.authorization = authorization;
return gson.toJson(container);
}
public Authorization fromJson(String json) {
Gson gson = new Gson();
Authorization.Container container = gson.fromJson(json, Authorization.Container.class);
return container.authorization;
}
This is the optimal code to do it in one pass.
MapWrapper class
public class MapWrapper {
private String language;
private Map<String, MyMapEntry> map;
public MapWrapper(String language, Map<String, MyMapEntry> map) {
this.language = language;
this.map = map;
}
}
MyMapEntry class
public class MyMapEntry {
String name;
public MyMapEntry(String name) {
this.name = name;
}
}
The Custom Deserializer
public class MyDeserialiser implements JsonDeserializer<MapWrapper>
{
#Override
public MapWrapper deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext ctx) throws JsonParseException {
JsonObject _global = json.getAsJsonObject();
_global = _global.get("root").getAsJsonObject();
JsonPrimitive lang = (JsonPrimitive) _global.get("language");
JsonElement map = _global.get("map");
Map<String, MyMapEntry> inMap = new LinkedHashMap<String, MyMapEntry>();
for (Entry<String, JsonElement> entry : map.getAsJsonObject()
.entrySet()) {
MyMapEntry _m = new MyMapEntry(entry.getValue().toString());
inMap.put(entry.getKey(), _m);
}
return new MapWrapper(lang.getAsString(), inMap);
}
}
Register it with GSON
new GsonBuilder().registerTypeAdapter(MapWrapper.class,new MyDeserialiser()).create()
Now deserialise with following code
String json; // your jsonString
MapWrapper result = ConvertJsonToObject.getFromJSON(json,MapWrapper.class);
My answer is late to this party.
Once we parse the Json, the container is always going to be a JsonObject subclass of JsonElement. Thus, if we want to skip it, we just need to cast it to its subclass and grab the field holding our inner class.
String response = ....;
Gson gson = new Gson();
JsonParser p = new JsonParser();
JsonElement jsonContainer = p.parse(response);
JsonElement jsonQuery = ((JsonObject) jsonContainer).get("query");
MyQuery query = gson.fromJson(jsonQuery, MyQuery.class);
Note: JsonObject and JSONObject are different classes (use the com.google.Json import).
You could generalize this answer more such that you wouldn't need to know the name of the inner class. You would do this by simply getting the one-and-only field of the container object. However, I see no way to do this other than starting up the iterator, there is no getValue(atIndex) method I can see, and I think starting an iterator is probably less efficient than simply looking up the field by name (but could be wrong).
The iterator method looks like:
JsonElement jsonQuery = ((JsonObject) jsonContainer).entrySet().iterator()
.next().getValue();
You could deserialize it into a Map<String, MapWrapper>.
Inspired by Gustav Carlson's idea I decided to expand it to a concrete sample. Here's a junit test that tests parsing this JSON as Map.
public static class MapWrapper {
private String language;
private Map<String, MyMapEntry> map;
}
public static class MyMapEntry {
String name;
}
#Test
public void testParsing() {
String json = "{\n" +
" \"root\": {\n" +
" \"language\": \"en\",\n" +
" \"map\": {\n" +
" \"k1\": {\n" +
" \"name\": \"n1\"\n" +
" },\n" +
" \"k2\": {\n" +
" \"name\": \"n2\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
Type type = new TypeToken<Map<String, MapWrapper>>(){}.getType();
Map<String, MapWrapper> parsed = gson.fromJson(json, type);
MapWrapper mapWrapper = parsed.get("root");
Assert.assertEquals("en", mapWrapper.language);
Assert.assertEquals("n2", mapWrapper.map.get("k2").name);
}
Related
At this point it's already an old question and I've probably read every related topic on SO.
But to the point. I need some advice or correction maybe?
For some reason we have generatable Jsons of 2 types:
{"data": {"id": "value"}} and {"data":[{"id": "value"}]}
Object and Array. There are also other params but they doesn't matter here. "id" is differ for every request. Sometimes it's userId, portfolioId etc. So I get "id" and pass it to related var.
For a long time I was working with the first case. And created POJO like this:
Data.class
public class Data {
#SerializedName("id")
#Expose
private String id;
public Data() {
}
public Data(String id) {
super();
this.id = id;
}
protected String getId() {
return id;
}
And I adress "data" paramets via User.class.
#JsonAdapter(UserDeserializer.class)
public Data data;
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public User() {
}
public User(Data data) {
super();
this.data = data;
}
Gson gson = new Gson();
public String getPortfolioList(String tokenId, String userId) {
Call<User> call = apiRequest.getPortfolioList(userId, tokenId);
try {
User newResult = gson.fromJson(String.valueOf(call.execute().body()), User.class);
System.out.println(newResult.getData().getId());
} catch (IOException e) {
e.printStackTrace();
}
return getPortfolioId();
}
Deserializer.class
public class UserDeserializer implements JsonDeserializer<User> {
private Type listType = new TypeToken<List<Data>>(){}.getType();
#Override
public User deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
User user = new User();
JsonElement jsonElement;
if (json.isJsonArray()) {
jsonElement = json.getAsJsonArray();
user.data = context.deserialize(jsonElement,listType);
// user.data = new Gson().fromJson(jsonElement, new TypeToken<List<Data>>() {}.getType());
} else {
jsonElement = json.getAsJsonObject();
user.data = context.deserialize(jsonElement, Data.class);
// user.setData(new Gson().fromJson(jsonElement, new TypeToken<Data>() {}.getType()));
}
return user;
}
}
Gson builder in BaseApi class just in case:
Gson gson = new GsonBuilder().registerTypeAdapter(UserDeserializer.class, new UserDeserializer()).setLenient().create();
Without custom deserialization and Array JSON issue this would work perfectly. But now I have to determine "data" 's exact type I get.
In above case I get java.lang.ClassCastException: java.util.ArrayList cannot be cast to auto.Rest.Data
I assume I have to create another Data class (for example there will be DataObject & DataArray) and describe every parameter as I did before in Data.class to get this work? I think I do something wrong during deserialization but I'm not sure where tbh.
Or am I wrong and it is possible to invoke Data as List and Data as an Object of the same class?
I'm working on this for several days already(?) and was thinking about use generics instead of Gson help, yeah, I'm desperate. So any help appreciated.
if there is always one object, just add
json.getAsJsonArray().get(0);
public class UserDeserializer implements JsonDeserializer<User> {
private Type listType = new TypeToken<List<Data>>(){}.getType();
#Override
public User deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
User user = new User();
JsonElement jsonElement;
if (json.isJsonArray()) {
jsonElement = json.getAsJsonArray().get(0);
user.data = context.deserialize(jsonElement,listType);
// user.data = new Gson().fromJson(jsonElement, new TypeToken<List<Data>>() {}.getType());
} else {
jsonElement = json.getAsJsonObject();
user.data = context.deserialize(jsonElement, Data.class);
// user.setData(new Gson().fromJson(jsonElement, new TypeToken<Data>() {}.getType()));
}
return user;
}
}
if there are more objects, change field data to the list
public class UserDeserializer implements JsonDeserializer<User> {
private Type listType = new TypeToken<List<Data>>(){}.getType();
#Override
public User deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
User user = new User();
JsonElement jsonElement;
if (json.isJsonArray()) {
jsonElement = json.getAsJsonArray();
user.data = context.deserialize(jsonElement,listType);
// user.data = new Gson().fromJson(jsonElement, new TypeToken<List<Data>>() {}.getType());
} else {
jsonElement = json.getAsJsonObject();
List<Data> data = new ArrayList<Data>();
data.add(context.deserialize(jsonElement, Data.class)) ;
user.data = data ;
// user.setData(new Gson().fromJson(jsonElement, new TypeToken<Data>() {}.getType()));
}
return user;
}
}
and change User.class field data to List
public List<Data> data;
this is a similar topic in kotlin language link
If you always have object or one-element array you can write custom deserialiser as below:
class OneOrElementJsonDeserializer<T> implements JsonDeserializer<T> {
#Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json instanceof JsonArray) {
final JsonArray array = (JsonArray) json;
final int size = array.size();
if (size == 0) {
return null;
}
return context.deserialize(array.get(0), typeOfT);
}
return context.deserialize(json, typeOfT);
}
}
Your example model after simplification looks like below:
class User {
#JsonAdapter(OneOrElementJsonDeserializer.class)
private Data data;
public User() {
}
public User(Data data) {
super();
this.data = data;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
#Override
public String toString() {
return "User{" +
"data=" + data +
'}';
}
}
class Data {
private String id;
protected String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public String toString() {
return "Data{" +
"id='" + id + '\'' +
'}';
}
}
Example usage:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.JsonAdapter;
import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Type;
public class GsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
User root = gson.fromJson(new FileReader(jsonFile), User.class);
System.out.println(root);
}
}
Above code for below JSON payload:
{
"data": [
{
"id": "c87ca3fe85781007869b83f"
}
]
}
prints:
User{data=Data{id='c87ca3fe85781007869b83f'}}
And for object case JSON payload:
{
"data": {
"id": "c87ca3fe85781007869b83f"
}
}
prints:
User{data=Data{id='c87ca3fe85781007869b83f'}}
In case your property could contain JSON object or multi-element array see my answer to this question Mapping Json Array to Java Models. There is implemented deserialiser which handle cases like this.
I have these JSON String:
{
"Results": {
"output1": {
"type": "table",
"value": {
"ColumnNames": ["userId", "documentId", "Scored Labels", "Scored Probabilities"],
"ColumnTypes": ["String", "String", "Boolean", "Double"],
"Values": [["100213199594809000000", "1Ktol-SWvAh8pnHG2O7HdPrfbEVZWX3Vf2YIPYXA_8gI", "False", "0.375048756599426"], ["103097844766994000000", "1jYsTPJH8gaIiATix9x34Ekcj31ifJMkPNb0RmxnuGxs", "True", "0.753859758377075"]]
}
}
}
}
And I want to have only the ColumnNames and the Values. I have tried it with something like this:
Map<String,Object> map = mapper.readValue(filename, Map.class);
String CN = (String) map.get("ColumnNames");
But then I get the following error:
Exception in thread "main" org.codehaus.jackson.JsonParseException: Unexpected character ('A' (code 65)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: java.io.StringReader#64232b15; line: 1, column: 2]`
I've worked only few times with JSON. Can anybody help me here?
The best case for me would be something like this, which I've done in another case:
String uId = (String) attr.get("userId");
Is it possible?
So now I've done this:
I try it like this:
public class ClientPOJO {
private String userId;
private String documentId;
public String getuserId() {
return userId;
}
public void setuserId(String userId) {
this.userId = userId;
}
public String getdocumentId() {
return documentId;
}
public void setdocumentId(String documentId) {
this.documentId = documentId;
}
}
and then:
ObjectMapper mapper = new ObjectMapper();
ClientPOJO clientes= mapper.readValue(filename, ClientPOJO.class);
String uid = clientes.getuserId();
But now when I make a Prtinout I'll get the same error like before:
Exception in thread "main" org.codehaus.jackson.JsonParseException: Unexpected character ('A' (code 65)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: java.io.StringReader#7a6eb29d; line: 1, column: 2]
Java- Convert JSON string into string / integer / Object
String jsonString = "{"username":"Gajender"}";
org.json.JSONObject jsonObj =new JSONObject(jsonString);
String name = (String) jsonObj.get("username").toString();
Below is an example to illustrate a generic approach to solve your problem ( based on Jackson library). You may like to enhance the solution to meet your all requirements.
Comments inlined.
package com.stackoverflow;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
// Junit class
public class TableDeserExample {
// sample input
String inputJson = "{\n" +
" \"Results\": {\n" +
" \"output1\": {\n" +
" \"type\": \"table\",\n" +
" \"value\": {\n" +
" \"ColumnNames\": [\"userId\", \"documentId\", \"Scored Labels\", \"Scored Probabilities\"],\n" +
" \"ColumnTypes\": [\"String\", \"String\", \"Boolean\", \"Double\"],\n" +
" \"Values\": [[\"100213199594809000000\", \"1Ktol-SWvAh8pnHG2O7HdPrfbEVZWX3Vf2YIPYXA_8gI\", \"False\", \"0.375048756599426\"], [\"103097844766994000000\", \"1jYsTPJH8gaIiATix9x34Ekcj31ifJMkPNb0RmxnuGxs\", \"True\", \"0.753859758377075\"]]\n"
+
" }\n" +
" }\n" +
" }\n" +
"}";
// POJO to map the Json structure. You may want to make it generalize based
// on field "type"
// (https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization)
public static class Result {
private String type;
private TableResult value;
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public void setValue(TableResult value) {
this.value = value;
}
public TableResult getValue() {
return this.value;
}
}
// Pojo for table result
public static class TableResult {
private List<String> columnNames;
private List<String> columnTypes;
private List<Object[]> values;
#JsonProperty("ColumnNames")
public List<String> getColumnNames() {
return this.columnNames;
}
public void setColumnNames(List<String> columnNames) {
this.columnNames = columnNames;
}
#JsonProperty("ColumnTypes")
public List<String> getColumnTypes() {
return this.columnTypes;
}
public void setColumnTypes(List<String> columnTypes) {
this.columnTypes = columnTypes;
}
#JsonProperty("Values")
public List<Object[]> getValues() {
return this.values;
}
public void setValues(List<Object[]> values) {
this.values = values;
}
}
// Top level Json POJO
public static class ResultContainer {
private Map<String, Result> results;
#JsonProperty("Results")
public Map<String, Result> getResults() {
return this.results;
}
public void setResults(Map<String, Result> results) {
this.results = results;
}
}
// A contract to map the result "values" to the expected object
public static interface ResultMapper<T> {
T map(TableResult map, Object[] row);
}
// Basic implementation for mapping user object from json "values[i]" array
public static class UserTableResultMapper implements ResultMapper<User> {
#Override
public User map(TableResult result, Object[] row) {
User user = new User();
// Here use any mapper logic based on column name
// Retrieved from result object.
// Below are for illustration only
user.setId(String.valueOf(row[0]));
user.setDocumentId(String.valueOf(row[1]));
return user;
}
}
// A result reader class
public static class ResultReader<T> implements Iterable<T> {
private TableResult result;
private ResultMapper<T> mapper;
public ResultReader(TableResult result, ResultMapper<T> mapper) {
this.result = result;
this.mapper = mapper;
}
#Override
public Iterator<T> iterator() {
final Iterator<Object[]> itr = result.getValues().iterator();
return new Iterator<T>() {
#Override
public void remove() {
throw new UnsupportedOperationException();
}
#Override
public T next() {
Object[] values = itr.next();
return mapper.map(result, values);
}
#Override
public boolean hasNext() {
return itr.hasNext();
}
};
};
}
public static class User {
private String id;
private String documentId;
// and others
public String getId() {
return this.id;
}
public void setDocumentId(String documentId) {
this.documentId = documentId;
}
public void setId(String id) {
this.id = id;
}
public String getDocumentId() {
return this.documentId;
}
}
#Test
public void simpleTest() throws Exception {
ObjectMapper mapper = new ObjectMapper();
ResultContainer file = mapper.readValue(inputJson, ResultContainer.class);
Result result = file.getResults().get("output1");
ResultReader<User> userResultReader = new ResultReader<>(result.getValue(), new UserTableResultMapper());
for (User user : userResultReader) {
System.out.println(user.getId() + " : " + user.getDocumentId());
}
}
}
If you know exactly the structure of your json (like the json you have post) then you can using Gson to get your object like this:
JsonParser parser = new JsonParser();
JsonObject json = (JsonObject) parser.parse("your_json_string_here");
String column = json.get("Results").getAsJsonObject().get("output1").getAsJsonObject().get("value").getAsJsonObject().get("ColumnNames").getAsJsonArray().toString();
String value = json.get("Results").getAsJsonObject().get("output1").getAsJsonObject().get("value").getAsJsonObject().get("Values").getAsJsonArray().toString();
System.out.println(column);
System.out.println(value);
If you need some things more generic then you can parse your json string to a HashMap<String, Object> then using recursion to read the HashMap and get the value you want.
Example (in my code, the type of Map will corresponding to a Json Object, type of List will corresponding to the Array in Json string):
Type type = new TypeToken<HashMap<String, Object>>() {}.getType();
Gson gson = new Gson();
HashMap<String, Object> map = gson.fromJson("your_json_string_here", type);
for (String key : map.keySet()) {
Object obj = map.get(key);
if (obj instanceof List) {
for (Object o : (List) obj) {
if (o instanceof Map) {
loop((Map) o);
} else {
System.out.println(key + " : " + o);
}
}
} else if (obj instanceof Map) {
loop((Map) obj);
} else {
System.out.println(key + " : " + obj);
}
}
}
private static void loop(Map<String, Object> map) {
for (String key : map.keySet()) {
Object obj = map.get(key);
if (obj instanceof List) {
for (Object o : (List) obj) {
if (o instanceof Map) {
loop((Map) o);
} else {
System.out.println(key + " : " + o);
}
}
} else if (obj instanceof Map) {
loop((Map) obj);
} else {
System.out.println(key + " : " + obj);
}
}
}
Neither Jackson nor any other library will parse the Values array into objects with client data like your POJO. You can achieve this by getting the raw tree of data in this JSON and constructing objects by iterating over the Values array inside this tree. Assuming the order of ColumnNames is fixed then you can parse with Jackson like this:
final ObjectMapper mapper = new ObjectMapper();
final JsonNode tree = mapper.readTree(json);
final JsonNode values = tree.findValue("Values");
final List<ClientPOJO> clients = new ArrayList<>();
for (JsonNode node : values) {
final ClientPOJO client = new ClientPOJO();
client.setUserId(node.get(0).asText());
client.setDocumentId(node.get(1).asText());
client.setScoredLabels(node.get(2).asBoolean());
client.setScoredProbabilities(node.get(3).asDouble());
clients.add(client);
}
Docs for JsonNode. Basically with findValue you can get another node deep into the tree, with get you can get array elements by index and with asText etc you parse a value in JSON into the appropriate type in Java.
Since you seem to be flexible in choice of JSON parsing library I would suggest Jackson 2 from com.fasterxml instead of Jackson 1 from org.codehaus that you tried.
I have a JSON string like this:
{
"r": [
{
"pic": "1.jpg",
"name": "Name1"
},
{
"pic": "2.jpg",
"name": "Name2"
},
{
"pic": "3.jpg",
"name": "Name3"
}
]
}
I want to parse to this POJO model:
public class Catalog {
#SerializedName("r")
#Expose
private List<JSONObject> r = new ArrayList<JSONObject>();
public List<JSONObject> getR() {
return r;
}
public void setR(List<JSONObject> r) {
this.r = r;
}
}
I am parsing this way:
Catalog cat = new Gson().fromJson(jsonString,Catalog.class);
But finally am getting this json
{
"r": [
{
"nameValuePairs": {}
},
{
"nameValuePairs": {}
},
{
"nameValuePairs": {}
}
]
}
Please note that I don't want to use com.google.gson.JsonObject.
I want to use org.json.JSONObject. How to achieve this because almost all of my code uses it?
As it was already mentioned in other answer and comments, you probably might not really want to use org.json.JSONObject for several reasons. But if it's a must for you, you just have to create your org.json.JSONObject-aware Gson instance.
final class JSONObjectJsonDeserializer
implements JsonDeserializer<JSONObject> {
// The implementation is fully thread-safe and can be instantiated once
private static final JsonDeserializer<JSONObject> jsonObjectJsonDeserializer = new JSONObjectJsonDeserializer();
// Type tokens are immutable values and therefore can be considered constants (and final) and thread-safe as well
private static final TypeToken<Map<String, Object>> mapStringToObjectTypeToken = new TypeToken<Map<String, Object>>() {
};
private JSONObjectJsonDeserializer() {
}
static JsonDeserializer<JSONObject> getJsonObjectJsonDeserializer() {
return jsonObjectJsonDeserializer;
}
#Override
public JSONObject deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context) {
// Convert the input jsonElement as if it were a Map<String, Object> (a generic representation for JSON objectS)
final Map<String, Object> map = context.deserialize(jsonElement, mapStringToObjectTypeToken.getType());
// And forward the map to the JSONObject constructor - it seems to accept it nice
return new JSONObject(map);
}
}
Gson is designed thread-safe and does not need to be instantiated every time serialization or deserialization is necessary:
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(JSONObject.class, getJsonObjectJsonDeserializer())
.create();
And finally:
final Catalog catalog = gson.fromJson(jsonString, Catalog.class);
out.println(catalog.getR());
with the following result:
[{"name":"Name1","pic":"1.jpg"}, {"name":"Name2","pic":"2.jpg"}, {"name":"Name3","pic":"3.jpg"}]
Anyway, I would suggest you to redesign your mappings model.
I think you don't need JSONObject.
Try this
// is wrapped class for serialized json.
public class JsonExample
{
List<Catalog> r;
}
public class Catalog {
private String pic;
private String name;
public String getPic() {
return pic;
}
public String getName() {
return name;
}
}
JsonExample example = new Gson().fromJson(json, JsonExample.class);
Additional - using JSONObject
JSONObject obj = new JSONObject(json);
JSONArray arr = obj.getJSONArray("r");
List<Catalog> cataList = new ArrayList<>();
for(int i = 0 ; i < arr.length() ; ++i)
{
cataList.add(new Catalog(arr.getJSONObject(i)));
}
public class Catalog {
private String pic;
private String name;
public Catalog(JSONObject obj) throws JSONException
{
pic = obj.getString("pic");
name = obj.getString("name");
}
public String getPic() {
return pic;
}
public String getName() {
return name;
}
}
I think in your case, usage of gson library is not required at all.
Only org.json can solve the entire problem.
E.g.:
JSONObject json = new JSONObject(jsonString);
JSONArray jsonArray = json.getJSONArray("r");
List<JSONObject> jsonList = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
jsonList.add(jsonArray.getJSONObject(i));
}
Catalog catalog = new Catalog();
catalog.setR(jsonList);
I'm trying to serialize/deserialize an object, that involves polymorphism, into JSON using Gson.
This is my code for serializing:
ObixBaseObj lobbyObj = new ObixBaseObj();
lobbyObj.setIs("obix:Lobby");
ObixOp batchOp = new ObixOp();
batchOp.setName("batch");
batchOp.setIn("obix:BatchIn");
batchOp.setOut("obix:BatchOut");
lobbyObj.addChild(batchOp);
Gson gson = new Gson();
System.out.println(gson.toJson(lobbyObj));
Here's the result:
{"obix":"obj","is":"obix:Lobby","children":[{"obix":"op","name":"batch"}]}
The serialization mostly works, except its missing the contents of inherited members (In particular obix:BatchIn and obixBatchout strings are missing).
Here's my base class:
public class ObixBaseObj {
protected String obix;
private String display;
private String displayName;
private ArrayList<ObixBaseObj> children;
public ObixBaseObj()
{
obix = "obj";
}
public void setName(String name) {
this.name = name;
}
...
}
Here's what my inherited class (ObixOp) looks like:
public class ObixOp extends ObixBaseObj {
private String in;
private String out;
public ObixOp() {
obix = "op";
}
public ObixOp(String in, String out) {
obix = "op";
this.in = in;
this.out = out;
}
public String getIn() {
return in;
}
public void setIn(String in) {
this.in = in;
}
public String getOut() {
return out;
}
public void setOut(String out) {
this.out = out;
}
}
I realize I could use an adapter for this, but the problem is that I'm serializing a collection of base class type ObixBaseObj. There are about 25 classes that inherits from this. How can I make this work elegantly?
There's a simple solution: Gson's RuntimeTypeAdapterFactory (from com.google.code.gson:gson-extras:$gsonVersion). You don't have to write any serializer, this class does all work for you. Try this with your code:
ObixBaseObj lobbyObj = new ObixBaseObj();
lobbyObj.setIs("obix:Lobby");
ObixOp batchOp = new ObixOp();
batchOp.setName("batch");
batchOp.setIn("obix:BatchIn");
batchOp.setOut("obix:BatchOut");
lobbyObj.addChild(batchOp);
RuntimeTypeAdapterFactory<ObixBaseObj> adapter =
RuntimeTypeAdapterFactory
.of(ObixBaseObj.class)
.registerSubtype(ObixBaseObj.class)
.registerSubtype(ObixOp.class);
Gson gson2=new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create();
Gson gson = new Gson();
System.out.println(gson.toJson(lobbyObj));
System.out.println("---------------------");
System.out.println(gson2.toJson(lobbyObj));
}
Output:
{"obix":"obj","is":"obix:Lobby","children":[{"obix":"op","name":"batch","children":[]}]}
---------------------
{
"type": "ObixBaseObj",
"obix": "obj",
"is": "obix:Lobby",
"children": [
{
"type": "ObixOp",
"in": "obix:BatchIn",
"out": "obix:BatchOut",
"obix": "op",
"name": "batch",
"children": []
}
]
}
EDIT: Better working example.
You said that there are about 25 classes that inherits from ObixBaseObj.
We start writing a new class, GsonUtils
public class GsonUtils {
private static final GsonBuilder gsonBuilder = new GsonBuilder()
.setPrettyPrinting();
public static void registerType(
RuntimeTypeAdapterFactory<?> adapter) {
gsonBuilder.registerTypeAdapterFactory(adapter);
}
public static Gson getGson() {
return gsonBuilder.create();
}
Every time we need a Gson object, instead of calling new Gson(), we will call
GsonUtils.getGson()
We add this code to ObixBaseObj:
public class ObixBaseObj {
protected String obix;
private String display;
private String displayName;
private String name;
private String is;
private ArrayList<ObixBaseObj> children = new ArrayList<ObixBaseObj>();
// new code
private static final RuntimeTypeAdapterFactory<ObixBaseObj> adapter =
RuntimeTypeAdapterFactory.of(ObixBaseObj.class);
private static final HashSet<Class<?>> registeredClasses= new HashSet<Class<?>>();
static {
GsonUtils.registerType(adapter);
}
private synchronized void registerClass() {
if (!registeredClasses.contains(this.getClass())) {
registeredClasses.add(this.getClass());
adapter.registerSubtype(this.getClass());
}
}
public ObixBaseObj() {
registerClass();
obix = "obj";
}
Why? because every time this class or a children class of ObixBaseObj is instantiated,
the class it's gonna be registered in the RuntimeTypeAdapter
In the child classes, only a minimal change is needed:
public class ObixOp extends ObixBaseObj {
private String in;
private String out;
public ObixOp() {
super();
obix = "op";
}
public ObixOp(String in, String out) {
super();
obix = "op";
this.in = in;
this.out = out;
}
Working example:
public static void main(String[] args) {
ObixBaseObj lobbyObj = new ObixBaseObj();
lobbyObj.setIs("obix:Lobby");
ObixOp batchOp = new ObixOp();
batchOp.setName("batch");
batchOp.setIn("obix:BatchIn");
batchOp.setOut("obix:BatchOut");
lobbyObj.addChild(batchOp);
Gson gson = GsonUtils.getGson();
System.out.println(gson.toJson(lobbyObj));
}
Output:
{
"type": "ObixBaseObj",
"obix": "obj",
"is": "obix:Lobby",
"children": [
{
"type": "ObixOp",
"in": "obix:BatchIn",
"out": "obix:BatchOut",
"obix": "op",
"name": "batch",
"children": []
}
]
}
I hope it helps.
I think that a custom serializer/deserializer is the only way to proceed and I tried to propose you the most compact way to realize it I have found. I apologize for not using your classes, but the idea is the same (I just wanted at least 1 base class and 2 extended classes).
BaseClass.java
public class BaseClass{
#Override
public String toString() {
return "BaseClass [list=" + list + ", isA=" + isA + ", x=" + x + "]";
}
public ArrayList<BaseClass> list = new ArrayList<BaseClass>();
protected String isA="BaseClass";
public int x;
}
ExtendedClass1.java
public class ExtendedClass1 extends BaseClass{
#Override
public String toString() {
return "ExtendedClass1 [total=" + total + ", number=" + number
+ ", list=" + list + ", isA=" + isA + ", x=" + x + "]";
}
public ExtendedClass1(){
isA = "ExtendedClass1";
}
public Long total;
public Long number;
}
ExtendedClass2.java
public class ExtendedClass2 extends BaseClass{
#Override
public String toString() {
return "ExtendedClass2 [total=" + total + ", list=" + list + ", isA="
+ isA + ", x=" + x + "]";
}
public ExtendedClass2(){
isA = "ExtendedClass2";
}
public Long total;
}
CustomDeserializer.java
public class CustomDeserializer implements JsonDeserializer<List<BaseClass>> {
private static Map<String, Class> map = new TreeMap<String, Class>();
static {
map.put("BaseClass", BaseClass.class);
map.put("ExtendedClass1", ExtendedClass1.class);
map.put("ExtendedClass2", ExtendedClass2.class);
}
public List<BaseClass> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
List list = new ArrayList<BaseClass>();
JsonArray ja = json.getAsJsonArray();
for (JsonElement je : ja) {
String type = je.getAsJsonObject().get("isA").getAsString();
Class c = map.get(type);
if (c == null)
throw new RuntimeException("Unknow class: " + type);
list.add(context.deserialize(je, c));
}
return list;
}
}
CustomSerializer.java
public class CustomSerializer implements JsonSerializer<ArrayList<BaseClass>> {
private static Map<String, Class> map = new TreeMap<String, Class>();
static {
map.put("BaseClass", BaseClass.class);
map.put("ExtendedClass1", ExtendedClass1.class);
map.put("ExtendedClass2", ExtendedClass2.class);
}
#Override
public JsonElement serialize(ArrayList<BaseClass> src, Type typeOfSrc,
JsonSerializationContext context) {
if (src == null)
return null;
else {
JsonArray ja = new JsonArray();
for (BaseClass bc : src) {
Class c = map.get(bc.isA);
if (c == null)
throw new RuntimeException("Unknow class: " + bc.isA);
ja.add(context.serialize(bc, c));
}
return ja;
}
}
}
and now this is the code I executed to test the whole thing:
public static void main(String[] args) {
BaseClass c1 = new BaseClass();
ExtendedClass1 e1 = new ExtendedClass1();
e1.total = 100L;
e1.number = 5L;
ExtendedClass2 e2 = new ExtendedClass2();
e2.total = 200L;
e2.x = 5;
BaseClass c2 = new BaseClass();
c1.list.add(e1);
c1.list.add(e2);
c1.list.add(c2);
List<BaseClass> al = new ArrayList<BaseClass>();
// this is the instance of BaseClass before serialization
System.out.println(c1);
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(al.getClass(), new CustomDeserializer());
gb.registerTypeAdapter(al.getClass(), new CustomSerializer());
Gson gson = gb.create();
String json = gson.toJson(c1);
// this is the corresponding json
System.out.println(json);
BaseClass newC1 = gson.fromJson(json, BaseClass.class);
System.out.println(newC1);
}
This is my execution:
BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0]
{"list":[{"total":100,"number":5,"list":[],"isA":"ExtendedClass1","x":0},{"total":200,"list":[],"isA":"ExtendedClass2","x":5},{"list":[],"isA":"BaseClass","x":0}],"isA":"BaseClass","x":0}
BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0]
Some explanations: the trick is done by another Gson inside the serializer/deserializer. I use just isA field to spot the right class. To go faster, I use a map to associate the isA string to the corresponding class. Then, I do the proper serialization/deserialization using the second Gson object. I declared it as static so you won't slow serialization/deserialization with multiple allocation of Gson.
Pro
You actually do not write more code than this, you let Gson do all the work. You have just to remember to put a new subclass into the maps (the exception reminds you of that).
Cons
You have two maps. I think that my implementation can refined a bit to avoid map duplications, but I left them to you (or to future editor, if any).
Maybe you want to unify serialization and deserialization into a unique object, you should be check the TypeAdapter class or experiment with an object that implements both interfaces.
I appreciate the other answers here that led me on my path to solving this issue. I used a combination of RuntimeTypeAdapterFactory with Reflection.
I also created a helper class to make sure a properly configured Gson was used.
Within a static block inside the GsonHelper class, I have the following code go through my project to find and register all of the appropriate types. All of my objects that will go through JSON-ification are a subtype of Jsonable.
You will want to change the following:
my.project in Reflections should be your package name.
Jsonable.class is my base class. Substitute yours.
I like having the field show the full canonical name, but clearly if you don't want / need it, you can leave out that part of the call to register the subtype. The same thing goes for className in the RuntimeAdapterFactory; I have data items already using the type field.
private static final GsonBuilder gsonBuilder = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting();
static {
Reflections reflections = new Reflections("my.project");
Set<Class<? extends Jsonable>> allTypes = reflections.getSubTypesOf(Jsonable.class);
for (Class< ? extends Jsonable> serClass : allTypes){
Set<?> subTypes = reflections.getSubTypesOf(serClass);
if (subTypes.size() > 0){
RuntimeTypeAdapterFactory<?> adapterFactory = RuntimeTypeAdapterFactory.of(serClass, "className");
for (Object o : subTypes ){
Class c = (Class)o;
adapterFactory.registerSubtype(c, c.getCanonicalName());
}
gsonBuilder.registerTypeAdapterFactory(adapterFactory);
}
}
}
public static Gson getGson() {
return gsonBuilder.create();
}
I created a type adapter factory that uses an annotation and ClassGraph to discover subclasses and supports multiple serialization styles (Type Property, Property, Array). See github for source code and maven coordinates.
I want my JSON to look like this:
{
"information": [{
"timestamp": "xxxx",
"feature": "xxxx",
"ean": 1234,
"data": "xxxx"
}, {
"timestamp": "yyy",
"feature": "yyy",
"ean": 12345,
"data": "yyy"
}]
}
Code so far:
import java.util.List;
public class ValueData {
private List<ValueItems> information;
public ValueData(){
}
public List<ValueItems> getInformation() {
return information;
}
public void setInformation(List<ValueItems> information) {
this.information = information;
}
#Override
public String toString() {
return String.format("{information:%s}", information);
}
}
and
public class ValueItems {
private String timestamp;
private String feature;
private int ean;
private String data;
public ValueItems(){
}
public ValueItems(String timestamp, String feature, int ean, String data){
this.timestamp = timestamp;
this.feature = feature;
this.ean = ean;
this.data = data;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getFeature() {
return feature;
}
public void setFeature(String feature) {
this.feature = feature;
}
public int getEan() {
return ean;
}
public void setEan(int ean) {
this.ean = ean;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
#Override
public String toString() {
return String.format("{timestamp:%s,feature:%s,ean:%s,data:%s}", timestamp, feature, ean, data);
}
}
I just missing the part how I can convert the Java object to JSON with Jackson:
public static void main(String[] args) {
// CONVERT THE JAVA OBJECT TO JSON HERE
System.out.println(json);
}
My Question is: Are my classes correct? Which instance do I have to call and how that I can achieve this JSON output?
To convert your object in JSON with Jackson:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(object);
I know this is old (and I am new to java), but I ran into the same problem. And the answers were not as clear to me as a newbie... so I thought I would add what I learned.
I used a third-party library to aid in the endeavor: org.codehaus.jackson
All of the downloads for this can be found here.
For base JSON functionality, you need to add the following jars to your project's libraries:
jackson-mapper-asl
and
jackson-core-asl
Choose the version your project needs. (Typically you can go with the latest stable build).
Once they are imported in to your project's libraries, add the following import lines to your code:
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
With the java object defined and assigned values that you wish to convert to JSON and return as part of a RESTful web service
User u = new User();
u.firstName = "Sample";
u.lastName = "User";
u.email = "sampleU#example.com";
ObjectMapper mapper = new ObjectMapper();
try {
// convert user object to json string and return it
return mapper.writeValueAsString(u);
}
catch (JsonGenerationException | JsonMappingException e) {
// catch various errors
e.printStackTrace();
}
The result should looks like this:
{"firstName":"Sample","lastName":"User","email":"sampleU#example.com"}
Just follow any of these:
For jackson it should work:
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(object);
//will return json in string
For gson it should work:
Gson gson = new Gson();
return Response.ok(gson.toJson(yourClass)).build();
You could do this:
String json = new ObjectMapper().writeValueAsString(yourObjectHere);
This might be useful:
objectMapper.writeValue(new File("c:\\employee.json"), employee);
// display to console
Object json = objectMapper.readValue(
objectMapper.writeValueAsString(employee), Object.class);
System.out.println(objectMapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(json));
You can use Google Gson like this
UserEntity user = new UserEntity();
user.setUserName("UserName");
user.setUserAge(18);
Gson gson = new Gson();
String jsonStr = gson.toJson(user);
Well, even the accepted answer does not exactly output what op has asked for. It outputs the JSON string but with " characters escaped. So, although might be a little late, I am answering hopeing it will help people! Here is how I do it:
StringWriter writer = new StringWriter();
JsonGenerator jgen = new JsonFactory().createGenerator(writer);
jgen.setCodec(new ObjectMapper());
jgen.writeObject(object);
jgen.close();
System.out.println(writer.toString());
Note: To make the most voted solution work, attributes in the POJO have to be public or have a public getter/setter:
By default, Jackson 2 will only work with fields that are either
public, or have a public getter method – serializing an entity that
has all fields private or package private will fail.
Not tested yet, but I believe that this rule also applies for other JSON libs like google Gson.
public class JSONConvector {
public static String toJSON(Object object) throws JSONException, IllegalAccessException {
String str = "";
Class c = object.getClass();
JSONObject jsonObject = new JSONObject();
for (Field field : c.getDeclaredFields()) {
field.setAccessible(true);
String name = field.getName();
String value = String.valueOf(field.get(object));
jsonObject.put(name, value);
}
System.out.println(jsonObject.toString());
return jsonObject.toString();
}
public static String toJSON(List list ) throws JSONException, IllegalAccessException {
JSONArray jsonArray = new JSONArray();
for (Object i : list) {
String jstr = toJSON(i);
JSONObject jsonObject = new JSONObject(jstr);
jsonArray.put(jsonArray);
}
return jsonArray.toString();
}
}