This question already has answers here:
How do I convert a JSON array into a Java List. I'm using svenson
(4 answers)
Closed 5 years ago.
I got JSON like
{
"items":[
{
"id":15
,"name":"abc"
}
,{
"id":16
,"name":"xyz%"
}
,{
"id":17
,"name":"qwerty"
}
,{
"id":18
,"name":"rudloph"
}
,{
"id":19
,"name":"jane"
}
,{
"id":20
,"name":"doe"
}
]
}
I have class which is like:
public class Foo {
public String id;
public String name;
}
And I want to convert this JSON into List<Foo>. How can i do this? Right now I am doing like:
List<Foo> fooList = new ArrayList<>();
JSONObject jsonObject = new JSONObject(json);
JSONArray araray = jsonObject.getJSONArray("items");
for(int i =0 ; i < araray.length();i++){
Foo dto = new Foo();
dto.setId(Long.valueOf((String) araray.getJSONObject(i).get("id")));
dto.setName((String) araray.getJSONObject(i).get("name"));
fooList.add(dto);
}
PS: Cannot change JSON. Jackson or Gson. Please let me know with code example.
i think you need to use the java json api and you need bind it manually
If you use gson, you may use TypeToken to load the json string into a custom Foo object.
List<Foo> objects = gson.fromJson(source, new TypeToken<List<Foo>>(){}.getType());
source can be String or BufferedReader.
You are having the wrong model for the JSON String. actually the list of Foo is inside another Model. Simply write another class like
public class Item {
List<Foo> items;
public Item() {
}
// getter setter
}
Now you can use com.fasterxml.jackson.databind.ObjectMapper like this
ObjectMapper mapper = new ObjectMapper();
Item item = mapper.readValue(json, Item.class);
for(Foo foo : item.getItems()) {
...
}
Try this with GSON.
Gson gson = new Gson();
Map<String, List<Foo>> objects = gson.fromJson(source,
new TypeToken<Map<String, List<Foo>>>(){}.getType());
List<Foo> list = objects.get("items");
Related
I have the following Java class
public static class LogItem {
public Long timestamp;
public Integer level;
public String topic;
public String type;
public String message;
}
and I want to convert an ArrayList<LogItem> into the following JSON string:
{"logitems":[
{"timestamp":1560924642000, "level":20, "topic":"websocket", "type":"status", "message":"connected (mobile)"},
...
]}`
I would like to do the following:
JSONArray logitems = new JSONArray();
for (DB_LogUtils.LogItem item : items) {
logitems.put(DB_LogUtils.asJSONObject(item)); // <----
}
JSONObject data = new JSONObject();
data.put("logitems", logitems);
webViewFragment.onInjectMessage(data.toString(), null);
where DB_LogUtils.asJSONObject is the following method
public static JSONObject asJSONObject(LogItem item) throws JSONException
{
JSONObject logitem = new JSONObject();
logitem.put("timestamp", item.timestamp);
logitem.put("level", item.level);
logitem.put("topic", item.topic);
logitem.put("type", item.type);
logitem.put("message", item.message);
return logitem;
}
but instead of doing this manually (like logitem.put("timestamp", item.timestamp);) I want to do this with Gson, so that I would end up with something like this
JSONArray logitems = new JSONArray();
for (DB_LogUtils.LogItem item : items) {
logitems.put(new Gson().toJSONObject(item)); // <----
}
JSONObject data = new JSONObject();
data.put("logitems", logitems);
webViewFragment.onInjectMessage(data.toString(), null);
in order to not have to edit the code at multiple points when the LogItem class changes.
But Gson().toJSONObject(...) does not exist, only Gson().toJson(...), which returns a String. I don't want to transition into a String only to then parse it with org.json.JSONObject.
I ended up using a second class
public static class LogItems {
public List<LogItem> logitems = new ArrayList<>();
}
which then let me change the whole code to
webViewFragment.onInjectMessage(new Gson().toJson(items), null);
where items would be of type LogItems.
In this case, creating the extra wrapper class was an overall benefit, but I'd still want to know how I can create such a JSONObject from a class by using Gson.
As far as i know it could be not possible without using for loop to iterate json string into array and store into map with same key.
But you can achieve your solution instead of passing dto pass the list of items into gson object as follow.
List<Object> list = new ArrayList<Object>();
list.add("1560924642000");
list.add(20);
list.add("websocket");
list.add("status");
list.add("connected (mobile)");
Gson gson = new Gson();
Map mp = new HashMap();
mp.put("ietams", list);
String json = gson.toJson(mp);
System.out.println(json);
output will be
{"logitems":["1560924642000",20,"websocket","status","connected (mobile)"]}
Hope this will help !
here is my pojo
public class Data{
List<Object> objects;
String owneruid;
}
if the out put is pure json like this
{"object":[{"p1":100,"p2":"name","p3":"sfa0","p4":300}],"owneruid":"owneruid"}
then iam able to convert with no worries but
here is my output
{
"object":"[{\"p1\":32,\"p3\":470,\"p3\":\"213\",\"p4\":\"name\"}]",
"owneruid":"6697729776330393738"
}
im converting a json string to string because to store in my db as it does not accept json so when i query returns like above so every time i need to fetch the value and convert it to json object and put it in list and display. can you suggest me a better approach.
And when i try to convert a list of custom classes to json using GSON
ArrayList<Object> list=new ArrayList<>();
Object object=new Object();
object.setP1(3);
object.setP2(4);
list.add(object);
Gson gson=new Gson();
String json = gson.toJson(list);
Required:
{"object":[{"p1":100,"p2":"name","p2":"sfa0","p4":300}],"owneruid":"owneruid"}
buts it ends like this
{"object":"[{\"p1\":313,\"p2\":470,\"p3\":\"1521739327417\",\"p4\":\"name\"}]","owneruid":"6697729776330393738"}
You have to use any json frameworks. E.g. Jackson or Gson. As alternative you could do smth. like this. Just evaluate JavaScript.
public static void main(String... args) throws ScriptException {
ScriptEngine js = new ScriptEngineManager().getEngineByName("javascript");
Object obj = js.eval("[{\"width\":313,\"height\":470,\"mediauid\":\"1521739327417\",\"mediatype\":\"image\"}]");
// res is either List<Object> or Map<String, Object>
Object res = convertIntoJavaObject(obj);
}
private static Object convertIntoJavaObject(Object obj) {
if (!(obj instanceof ScriptObjectMirror))
return obj;
ScriptObjectMirror mirror = (ScriptObjectMirror)obj;
if (mirror.isArray())
return mirror.entrySet().stream()
.map(entry -> convertIntoJavaObject(entry.getValue()))
.collect(Collectors.toList());
Map<String, Object> map = new HashMap<>();
mirror.entrySet().forEach((key, value) -> map.put(key, convertIntoJavaObject(value)));
return map;
}
You can use the below code snippet as it seems fit for your case.
ObjectMapper can be found with Jackson framework. inputJson is the JSON string you have mentioned.
ObjectMapper mapper = new ObjectMapper();
Object mediaMetaDataObj = mapper.readValue( inputJson, Object.class );
Hope this helps.
I am needing to parse JSON data coming in from an outside source. The problem is sometimes and array of data is sent in and sometimes it come in as a single object, but the array and the single object have the same name.
{
"OuterObject": {
"Names":[
{
"name": "John Doe"
},
{
"name": "William Watson"
}
]
}
}
But when the JSON array has only one element, it looks like this:
{
"OuterObject": {
"Names": {
"name": "John Doe"
}
}
}
My application needs to be able to handle either one of these, but not both at the same time.
This is what my Json parsed class looks like:
#JsonRootName("OuterObject")
public class OuterObject {
#JsonProperty("Names")
private Names names;
#JsonProperty("Names")
private List<Names> namesList;
public Names getNames() {
return names;
}
public void setNames(Names names) {
this.names = names;
}
public List<Names> getNamesList() {
return namesList;
}
public void setNamesList(List<Names> namesList) {
this.namesList = namesList;
}
}
However, it doesn't look like it will work to have the same json property name for both the list and the single object. It also doesn't appear to just use an array and have the single json object parse into the list. Does anyone know of any ways that my application can handle both json arrays and single json objects when the arrays and the objects have the same name?
You just need to use a single field of type List<Names>, and activate the feature ACCEPT_SINGLE_VALUE_AS_ARRAY
YourClass result = mapper.reader(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.forType(YourClass.class)
.readValue(json);
I have used following method for convert JSONArray, if it is only one JSONObject.
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
private JSONArray getJSONArray(JSONObject json, String field) {
JSONArray array;
if(json.get(field) instanceof JSONObject){
array = new JSONArray();
array.add(json.get(field));
}else{
array = json.getJSONArray(field);
}
return array;
}
Convert your json to Map then use your code to get the desired result.
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.convertValue(json, Map.class);
or better
Map<String, Object> map = mapper.convertValue(json, new TypeReference<Map<String, Object>>() {});
I have this structure of my JSON response string:
{
"1":{
"data1":"1","data2":"test1", ...
},
"2":{
"data1":"6","data2":"test2", ...
},
...
}
And I want to get the values to put into an ArrayList<MyItem>. I use GSON and normally I can do it in this way:
ArrayList<MyItem> items =
gson.fromJson(jsonString, new TypeToken<ArrayList<MyItem>>() {}.getType());
The problem is, that it does not work, because my JSON String has numbers as keys, but I only want to get the values to put into the ArrayList (unfortunately, the JSON string can not be changed by myself). How can I do this efficiently?
I'd probably deserialize the JSON into a java.util.Map, get the values from the Map as a Collection using the Map.values() method, and then create a new ArrayList using the constructor that takes a Collection.
Write a custom deserializer.
class MyItem
{
String data1;
String data2;
// ...
}
class MyJSONList extends ArrayList<MyItem> {}
class MyDeserializer implements JsonDeserializer<MyJSONList>
{
public MyJSONList deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
{
MyJSONList list = new MyJSONList();
for (Entry<String, JsonElement> e : je.getAsJsonObject().entrySet())
{
list.add((MyItem)jdc.deserialize(e.getValue(), MyItem.class));
}
return list;
}
}
Example:
String json = "{\"1\":{\"data1\":\"1\",\"data2\":\"test1\"},\"2\":{\"data1\":\"6\",\"data2\":\"test2\"}}";
Gson g = new GsonBuilder()
.registerTypeAdapter(MyJSONList.class, new MyDeserializer())
.create();
MyJSONList l = g.fromJson(json, MyJSONList.class);
for (MyItem i : l)
{
System.out.println(i.data2);
}
Output:
test1test2
Seems like Gson.toJson(Object object) generates JSON code with randomly spread fields of the object. Is there way to fix fields order somehow?
public class Foo {
public String bar;
public String baz;
public Foo( String bar, String baz ) {
this.bar = bar;
this.baz = baz;
}
}
Gson gson = new Gson();
String jsonRequest = gson.toJson(new Foo("bar","baz"));
The string jsonRequest can be:
{ "bar":"bar", "baz":"baz" } (correct)
{ "baz":"baz", "bar":"bar" } (wrong sequence)
You'd need to create a custom JSON serializer.
E.g.
public class FooJsonSerializer implements JsonSerializer<Foo> {
#Override
public JsonElement serialize(Foo foo, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.add("bar", context.serialize(foo.getBar());
object.add("baz", context.serialize(foo.getBaz());
// ...
return object;
}
}
and use it as follows:
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, new FooJsonSerializer()).create();
String json = gson.toJson(foo);
// ...
This maintains the order as you've specified in the serializer.
See also:
Gson User Guide - Custom serializers and deserializers
If GSON doesn't support definition of field order, there are other libraries that do. Jackson allows definining this with #JsonPropertyOrder, for example. Having to specify one's own custom serializer seems like awful lot of work to me.
And yes, I agree in that as per JSON specification, application should not expect specific ordering of fields.
Actually Gson.toJson(Object object) doesn't generate fields in random order. The order of resulted json depends on literal sequence of the fields' names.
I had the same problem and it was solved by literal order of properties' names in the class.
The example in the question will always return the following jsonRequest:
{ "bar":"bar", "baz":"baz" }
In order to have a specific order you should modify fields' names, ex: if you want baz to be first in order then comes bar:
public class Foo {
public String f1_baz;
public String f2_bar;
public Foo ( String f1_baz, String f2_bar ) {
this.f1_baz = f1_baz;
this.f2_bar = f2_bar;
}
}
jsonRequest will be { "f1_baz ":"baz", "f2_bar":"bar" }
Here's my solution for looping over json text files in a given directory and writing over the top of them with sorted versions:
private void standardizeFormat(File dir) throws IOException {
File[] directoryListing = dir.listFiles();
if (directoryListing != null) {
for (File child : directoryListing) {
String path = child.getPath();
JsonReader jsonReader = new JsonReader(new FileReader(path));
Gson gson = new GsonBuilder().setPrettyPrinting().registerTypeAdapter(LinkedTreeMap.class, new SortedJsonSerializer()).create();
Object data = gson.fromJson(jsonReader, Object.class);
JsonWriter jsonWriter = new JsonWriter(new FileWriter(path));
jsonWriter.setIndent(" ");
gson.toJson(data, Object.class, jsonWriter);
jsonWriter.close();
}
}
}
private class SortedJsonSerializer implements JsonSerializer<LinkedTreeMap> {
#Override
public JsonElement serialize(LinkedTreeMap foo, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
TreeSet sorted = Sets.newTreeSet(foo.keySet());
for (Object key : sorted) {
object.add((String) key, context.serialize(foo.get(key)));
}
return object;
}
}
It's pretty hacky because it depends on the fact that Gson uses LinkedTreeMap when the Type is simply Object. This is an implementation details that is probably not guaranteed. Anyway, it's good enough for my short-lived purposes...