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.
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 happily been using Google Gson to parse extract some JSON metadata of the form
{
"lowlevel": {
"average_loudness": 0.570070445538
},
"rhythm": {
"beats_count": 502,
"bpm": 128.347702026
},
"tonal": {
"chords_changes_rate": 0.0534749031067
"tuning_diatonic_strength": 0.431238204241,
"tuning_equal_tempered_deviation": 0.164615109563,
"tuning_frequency": 434.193115234,
"tuning_nontempered_energy_ratio": 0.847496032715
}
}
Using this
public class AcousticBrainzLowlevelWrapper
{
private AcousticBrainzLowLevelRhythm rhythm;
private AcousticBrainzLowLevelTonal tonal;
public AcousticBrainzLowLevelRhythm getRhythm()
{
return rhythm;
}
public void setRhythm(AcousticBrainzLowLevelRhythm rhythm)
{
this.rhythm = rhythm;
}
public AcousticBrainzLowLevelTonal getTonal()
{
return tonal;
}
public void setTonal(AcousticBrainzLowLevelTonal tonal)
{
this.tonal = tonal;
}
}
and
AcousticBrainzLowlevelWrapper low = gson.fromJson(result, AcousticBrainzLowlevelWrapper.class) ;
(Full JSON can be seen here)
but now the API has been extended to allow multiple lookups such as this url
which now returns
{
"96685213-a25c-4678-9a13-abd9ec81cf35": {
"0": {
"lowlevel": {
"average_loudness": 0.570070445538
},
"rhythm": {
"beats_count": 502,
"bpm": 128.347702026
},
"tonal": {
"chords_changes_rate": 0.0534749031067
"tuning_diatonic_strength": 0.431238204241,
"tuning_equal_tempered_deviation": 0.164615109563,
"tuning_frequency": 434.193115234,
"tuning_nontempered_energy_ratio": 0.847496032715
}
}
.....
"78787888-a25c-4678-9a13-abd9ec81cf35": {
"0": {
"lowlevel": {
......
..
The difference being that the json doesn't define what "96685213-a25c-4678-9a13-abd9ec81cf35" and "78787888-a25c-4678-9a13-abd9ec81cf35" are, or what "0" is.
So I know what they represent (MusicBrainzRecording and offset) but I cannot create a class like AcousticBrainzLowlevelWrapper to represent this, so how do I parse this new api.
Update
I tried creating
public class AcousticBrainzLowLevelList
{
private Map<String, AcousticBrainzLowlevelWrapper> data = new HashMap<>();
public Map<String, AcousticBrainzLowlevelWrapper> getData()
{
return data;
}
public void setData(Map<String, AcousticBrainzLowlevelWrapper> data)
{
this.data = data;
}
}
and then calling
AcousticBrainzLowLevelList lowMap = gson.fromJson(result, AcousticBrainzLowLevelList.class) ;
but nothing get added to the map. Unsuprisingly because data I dont' see how can i give a name since there is no consistent name at the top level.
It seems to me that your input JSON could be parsed to produce a Java class of type Map<String,Map<Integer,AcousticBrainzLowlevelWrapper>> :
Type type = new TypeToken<Map<String,Map<Integer,AcousticBrainzLowlevelWrapper>>>(){}.getType();
Map<String,Map<Integer,AcousticBrainzLowlevelWrapper>> result = gson.fromJson(json, type);
As I wrote it, I might as well post it:
Similar to Maurice's answer
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Map;
public class Main {
private final static String jsonSingle =
"{ \"attribute1\": \"value1\", \"attribute2\": \"value2\" }";
private final static String jsonMultiple =
"{\n" +
" \"96685213-a25c-4678-9a13-abd9ec81cf35\": {\n" +
" \"0\": { \"attribute1\": \"value1\", \"attribute2\": \"value2\" }\n" +
" },\n" +
" \"78787888-a25c-4678-9a13-abd9ec81cf35\": {\n" +
" \"0\": { \"attribute1\": \"value3\", \"attribute2\": \"value4\" }\n" +
"}}";
public static void main(String[] args) {
MyBean bean = new Gson().fromJson(jsonSingle, MyBean.class);
System.out.println(bean);
Type type = new TypeToken<Map<String, Map<String, MyBean>>>(){}.getType();
Map<String, String> myMap = new Gson().fromJson(jsonMultiple, type);
System.out.println(myMap);
}
}
MyBean class:
class MyBean {
String attribute1;
String attribute2;
public String getAttribute1() {
return attribute1;
}
public void setAttribute1(String attribute1) {
this.attribute1 = attribute1;
}
public String getAttribute2() {
return attribute2;
}
public void setAttribute2(String attribute2) {
this.attribute2 = attribute2;
}
#Override
public String toString() {
return "MyBean: <attribute1: " + attribute1 + " | " + "attribute2: " + attribute2 + ">";
}
}
Outputs:
MyBean: <attribute1: value1 | attribute2: value2>
and
{96685213-a25c-4678-9a13-abd9ec81cf35={0=MyBean: <attribute1: value1 | attribute2: value2>}, 78787888-a25c-4678-9a13-abd9ec81cf35={0=MyBean: <attribute1: value3 | attribute2: value4>}}
I have a bunch of JSON objects in a string notation:
"{\"address\":{\"street\":\"Steenstraat\",\"housenumber\":\"17A\",\"postalcode\":\"6828 CA\",\"city\":\"ARNHEM\",\"geoLocation\":{\"lat\":\"51.983718\",\"lng\":\"5.915553\"}},\"distance\":0,\"type\":\"ING\"}
So each of the JSON object looks something like this:
{
"address" : {
"street" : "Steenstraat",
"housnumber" : "17A",
"postalcode" : "6828 CA",
"city" : "ARNHEM",
"geolocation" : {
"latitude" : "51.983718",
"longitude" : "54.983718"
}
},
"type" : "citi",
"distance" : 0
}
Now, I used google's gson library to get from the rest API and that has given me a string of many of the above JSON objects. How can I try to filter out (or redefine the structure of the JSON) to sort the JSON objects by a particular parameter (say sort by city names)?
This is my Atm class. I'm trying to convert the JSON string to a list of Atm objects.
public class Atm {
private String type;
private Long distance;
private Map<String, String> address = new HashMap<String, String>();
public Atm() {
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Long getDistance() {
return distance;
}
public void setDistance(Long distance) {
this.distance = distance;
}
public Map<String, String> getAddress() {
return address;
}
public void setAddress(Map<String, String> address) {
this.address = address;
}
#Override
public String toString() {
return "Atm{" +
"type='" + type +
", distance=" + distance +
", address=" + address.toString() +
'}';
}
}
Or is there a way to do this without converting it into java data structures?
Note: As you are using a subelement geoLocation in your JSON, you cannot use Map<String, String>.
You should use: Map<String, Object> instead or create a custom class to represent your address.
To filter your Atm list by city you could do the following.
In Java 8:
Gson gson = new Gson();
String atm1 = "{\"address\":{\"street\":\"Steenstraat\",\"housenumber\":\"17A\",\"postalcode\":\"6828 CA\",\"city\":\"ARNHEM\",\"geoLocation\":{\"lat\":\"51.983718\",\"lng\":\"5.915553\"}},\"distance\":0,\"type\":\"ING\"}";
String atm2 = "{\"address\":{\"street\":\"Steenstraat\",\"housenumber\":\"17A\",\"postalcode\":\"6828 CA\",\"city\":\"ARNHEM\",\"geoLocation\":{\"lat\":\"51.983718\",\"lng\":\"5.915553\"}},\"distance\":0,\"type\":\"ING\"}";
String atm3 = "{\"address\":{\"street\":\"Steenstraat\",\"housenumber\":\"17A\",\"postalcode\":\"6828 CA\",\"city\":\"NEW-YORK\",\"geoLocation\":{\"lat\":\"51.983718\",\"lng\":\"5.915553\"}},\"distance\":0,\"type\":\"ING\"}";
List<Atm> atms = new ArrayList<Atm>();
atms.add(gson.fromJson(atm1, Atm.class));
atms.add(gson.fromJson(atm2, Atm.class));
atms.add(gson.fromJson(atm3, Atm.class));
List<Atm> filteredOnCity = atms.stream().filter(atm -> atm.getAddress().get("city")
.equals("ARNHEM")).collect(Collectors.toList());
With Apache commons-collections4:
//Build your list with code from above
Predicate<Atm> filterOnCity = new Predicate<Atm>() {
#Override
public boolean evaluate(Atm atm) {
return atm.getAddress().get("city").equals("ARNHEM");
}
};
CollectionUtils.filter(atms, filterOnCity);
Why not filter or sort them on the client side before sending them to the Java?
var arr = JSON.parse(MY_JSON_ARRAY_STRING);
arr.sort(function(a, b){
if ( a.city < b.city )
return -1;
if ( a.city > b.city )
return 1;
return 0;
});
var arrString = JSON.stringify(arr);
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);
}
I have a JSON response that I try to parse with Jackson, and type of one of fields is unknown.
Exapmle:
{"name" : "Catalina"}
OR
{"name" : {"First" : "Catalina", "Last" : "Kyle"}}
How I can deserialize that object into POJO:
class NamesHolder {
public String singleName;
public Map<String, String> nameMap;
}
This is synthetic example but I hope it clear for understanding.
In first case I want to have object with singleName == "Catalina" and nameMap == null, and vice versa in other case.
UPDATE
I am really sorry, at the end of day I misspelled the property name. In both cases they are the same: name.
Try making your name field an object:
class MyPojo {
public Object name;
}
The you'll have to check at runtime to see if it deserialized as a String or as a Map.
The easiest way is to rename your POJO properties to the same which you have in JSON. See below example:
class NamesHolder {
public String name;
public Map<String, String> names;
#Override
public String toString() {
return "NamesHolder{" +
"name='" + name + '\'' +
", names=" + names +
'}';
}
}
If you do not want to rename properties you can use #JsonProperty annotation. See below example:
class NamesHolder {
#JsonProperty("name")
public String singleName;
#JsonProperty("names")
public Map<String, String> nameMap;
#Override
public String toString() {
return "NamesHolder{" +
"name='" + singleName + '\'' +
", names=" + nameMap +
'}';
}
}
Two above classes work for your JSON examples. See below program:
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue("{\"names\" : {\"First\" : \"Catalina\", \"Last\" : \"Kyle\"}}", NamesHolder.class));
System.out.println(mapper.readValue("{\"name\" : \"Catalina\"}", NamesHolder.class));
which prints:
NamesHolder{name='null', names={First=Catalina, Last=Kyle}}
NamesHolder{name='Catalina', names=null}
EDIT 1
In this case you have to handle it manually using Object type. See below example:
public class JacksonTest {
public static void main(String[] args) throws Exception {
String json = "{\"name\" : \"Catalina\"}";
ObjectMapper deserializerMapper = new ObjectMapper();
NamesHolder namesHolder = deserializerMapper.readValue(json, NamesHolder.class);
System.out.println(toString(namesHolder));
json = "{\"name\" : {\"First\" : \"Catalina\", \"Last\" : \"Kyle\"}}";
namesHolder = deserializerMapper.readValue(json, NamesHolder.class);
System.out.println(toString(namesHolder));
}
private static String toString(NamesHolder namesHolder) {
if (namesHolder.hasStringName()) {
return "Type: String, Value: " + namesHolder.getStringName();
} else if (namesHolder.hasMapNames()) {
return "Type: Map, Value: " + namesHolder.getMapNames();
}
return "Type: Unknown, Value: " + namesHolder;
}
}
class NamesHolder {
public Object name;
public boolean hasStringName() {
return name instanceof String;
}
public String getStringName() {
return name.toString();
}
public boolean hasMapNames() {
return name instanceof Map;
}
public Map<String, String> getMapNames() {
return (Map<String, String>) name;
}
#Override
public String toString() {
return String.valueOf(name);
}
}
Above example prints:
Type: String, Value: Catalina
Type: Map, Value: {First=Catalina, Last=Kyle}