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.
Related
I have one specific case. I need to serialize/deserialize an object to Map<String, Object>. I have a class that looks like the following:
public class Data {
public String name;
public Map<String, Object> options = new HashMap<>();
}
I can put to this options objects of any type. For instance:
public class Option {
public int id;
...
}
public class TestOpt {
public name;
...
}
and I try to serialize and deserialize it:
public static void main(String... args) {
ObjectMapper mapper = new ObjectMapper();
Option o = new Option();
o.id = 1;
TestOpt t = new TestOpt();
t.name = "fff";
Data data = new Data();
data.name = "data";
data.options.put("o", o);
data.options.put("t", t);
String json = mapper.writeValueAsString(data);
Data d1 = mapper.readValue(json, Data.class);
// I get error because options.get("o") contains LinkedHashMap instead of Option.class
System.out.println(((Option)d1.options.get("o")).id);
}
How can I fix this issue?
The value of the serialized json is
{"name":"data","options":{"t":{"name":"fff"},"o":{"id":1}}}
So, the problem is that the object mapper has no way to tell that the o value inside the json is an Option. The best guess is that it could be a map and thus it is deserialized as a LinkedHashMap.
If you are sure that the element o is an Option, you can convert the value using an object mapper:
Option option = mapper.convertValue(d1.options.get("o"), Option.class);
But please note, that this means that the value is again serialized and then deserialized using the right type information. You can do that, but it is not a good solution.
If it is possible, a better way would be to change your model from a generic map to a specific class that contains the type information:
class Data {
public String name;
public DataOptions options = new DataOptions();
}
class DataOptions {
public Option o;
public TestOpt t;
}
Serializing this model has the same json representation as the model using a map, and the model can be used to deserialize the json from your example.
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 !
I'm trying to deserialize the following structure
{ meta: { keywords: [a, b, c, d]} ... }
other valid structures are
{ meta: { keywords: "a,b,c,d"} ... }
and
{ meta: {keywords: "a"} ...}
I have this classes
public class Data {
#PropertyName("meta")
MetaData meta;
...
}
public class MetaData {
List<String> keywords;
...
}
and a custom deserializer
public static class CustomDeserilizer implements JsonDeserializer<MetaData>{
#Override
public MetaData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
List<String> keywords = null;
Gson gson = new Gson();
MetaData metaData = gson.fromJson(json, AppMetaData.class);
JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("keywords")) {
JsonElement elem = jsonObject.get("keywords");
if (elem != null && !elem.isJsonNull()) {
if (jsonObject.get("keywords").isJsonArray()) {
keywords = gson.fromJson(jsonObject.get("keywords"), new TypeToken<List<String>>() {
}.getType());
} else {
String keywordString = gson.fromJson(jsonObject.get("keywords"), String.class);
keywords = new ArrayList<String>();
list.addAll(Arrays.asList(keywordString.split(",")));
}
}
}
metaData.setKeywords(keywords);
}
Then I try to apply the deserilizer:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Data.class,new CustomDeserilizer())
.create();
But I get a parsing error , because is trying to deserialize Data instead of MetaData, how can I apply this deserializer to make it work right?
I solved it creating a deserializer for my class Data.
public static class DataDeserilizer implements JsonDeserializer {
#Override
public Data deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Gson gson = new Gson();
Data data = gson.fromJson(json, Data.class);
JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("meta")) {
JsonElement elem = jsonObject.get("meta");
if (elem != null && !elem.isJsonNull()) {
Gson gsonDeserializer = new GsonBuilder()
.registerTypeAdapter(MetaData.class, new CustomDeserilizer())
.create();
gsonDeserializer.fromJson(jsonObject.get("meta"), Data.class);
}
}
return data;
}
}
And
Gson gson = new GsonBuilder()
.registerTypeAdapter(Data.class,new DataDeserilizer())
.create();
Pretty obvious, but is there a more elegant solution?
Firstly, rename your class to meta instead of metadata and make keywords String instead of List.Then use the following to map your JSonString into your object.
Gson gson = new GsonBuilder().create();
Meta meta = gson.from(yourJsonString,Meta.class);
In order to get keywords only, you need this.
JsonObject jsonObject = new JsonObject(yourJSonString);
String data = jsonObject.getJsonObject("meta").getString("keywords");
keywords is a JsonObject not an JsonArray so you can't directly map it
onto List. You can split the string to get keywords in an array.
String keywords[] = data.split(",");
Here's a concise solution that leverages Java inheritance to represent the nested structure; and therefore does not need to provide any actual instance member fields (mappings, etc) for capturing the nested String data that GSON maps.
Step 1: For readability, create an empty object to represent the nested mapping
public class StateRegionCitiesMap extends HashMap<String, List<String>> {
}
Step 2: Add the one line of actual code to do the mapping; no other serialize/deserialize logic to manage
protected void loadContent(JsonObject stateRegionsJsonObject) {
HashMap<String, StateRegionCitiesMap> stateRegionCitiesMap =
mGson.fromJson(
stateRegionsJsonObject,
new TypeToken<HashMap<String, StateRegionCitiesMap>>() {
}.getType()
);
}
Alternatively, you can skip the wrapper class altogether and just directly put <String, List<String>> in the GSON call. However, I find an explicit object helps to inform/remind whoever is reading the code, what the purpose is.
Example JSON:
The class StateRegionCitiesMap represents a multi-tier map structure for say:
[US State] -> [State-Region Key] -> [Sub-Region Key] -> CitiesArray[]
"CA": {
"Central CA": {
"Central Valley": [
"FRESNO",
"VISALIA"
],
"Sacramento Area": [
"SACRAMENTO",
"EL DORADO HILLS"
]
},
This suppose to achieve what you want easily. You should define an inner static class. You can keep nesting classes to define keywords as class Keywords, etc. Just remember to have a field in the containing class, i.e.
in your inner class have private Keywords keywords;
In your Main class:
Gson gson = new Gson();
Data data = gson.fromJson(SOME_JSON_STRING, Data.class);
In a class called Data:
public class Data {
private Meta meta;
static class Meta{
private String[] keywords;
}
}
I have a Map<String, Object> which contains a deserialized form of JSON. I would like to deserialize this into the fields of a POJO.
I can perform this using Gson by serializing the Map into a JSON string and then deserializing the JSON string into the POJO, but this is inefficient (see example below). How can I perform this without the middle step?
The solution should preferably use either Gson or Jackson, as they're already in use by the project.
Example code:
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
public class Test {
public static void main(String[] args) {
Map<String, Object> innermap = new HashMap<String, Object>();
innermap.put("number", 234);
innermap.put("string", "bar");
Map<String, Object> map = new HashMap<String, Object>();
map.put("number", 123);
map.put("string", "foo");
map.put("pojo2", innermap);
Gson gson = new Gson();
// How to perform this without JSON serialization?
String json = gson.toJson(map);
MyPojo pojo = gson.fromJson(json, MyPojo.class);
System.out.println(pojo);
}
}
class MyPojo {
private int number;
private String string;
private MyPojo pojo2;
#Override
public String toString() {
return "MyPojo[number=" + number + ", string=" + string + ", pojo2=" + pojo2 + "]";
}
}
Using Gson, you can turn your Map into a JsonElement, which you can then parse in exactly the same way using fromJson:
JsonElement jsonElement = gson.toJsonTree(map);
MyPojo pojo = gson.fromJson(jsonElement, MyPojo.class);
This is similar to the toJson method that you are already using, except that it avoids writing the JSON String representation and then parsing that JSON String back into a JsonElement before converting it to your class.
In Jackson you can use convertValue method. See below example:
mapper.convertValue(map, MyPojo.class);
You can use jackson library for convert map object to direct POJO.
Map<String, String> map = new HashMap<>();
map.put("id", "5");
map.put("name", "Bob");
map.put("age", "23");
map.put("savings", "2500.39");
ObjectMapper mapper = new ObjectMapper();
MyPojo pojo = mapper.convertValue(map, MyPojo.class);
You can directly construct MyPojo by giving it the map.
Something like
MyPojo pojo = new MyPojo(map);
And declaring an according constructor :
public MyPojo(Map<String, Object> map){
this.number=map.get("number");
this.string=map.get("string");
this.pojo2=new MyPojo(); // I don't know if this is possible
this.pojo2.string=map.get("pojo2").get("string");
this.pojo2.number=map.get("pojo2").get("number");
}
My question is very similiar to this:
Unable to parse Json array using Gson
But I cann't get the answer from it.
The answer from above link:
public static List<MapData> getData(){
Gson gson = new Gson();
String jsonString = "[{\"id\":18,\"city\":\"test\",\"street\":\"test 1\",\"zipcode\":121209,\"state\":\"IL\",\"lat\":32.158138,\"lng\":34.807838},{\"id\":19,\"city\":\"test\",\"street\":\"1\",\"zipcode\":76812,\"state\":\"IL\",\"lat\":32.161041,\"lng\":34.810410}]";
Type type = new TypeToken<List<MapData>>(){}.getType();
return gson.fromJson(jsonString, type);
}
It works well, but I want to use implicit operator on generic type. See below:
public static <T> List<T> getData(Class<T> classT){
Gson gson = new Gson();
String jsonString = "[{\"id\":18,\"city\":\"test\",\"street\":\"test 1\",\"zipcode\":121209,\"state\":\"IL\",\"lat\":32.158138,\"lng\":34.807838},{\"id\":19,\"city\":\"test\",\"street\":\"1\",\"zipcode\":76812,\"state\":\"IL\",\"lat\":32.161041,\"lng\":34.810410}]";
Type type = new TypeToken<List<T>>(){}.getType();
return gson.fromJson(jsonString, type);
}
And then I try to pass the Class argument to the method:
List<MapData> data = getData(MapData.class);
System.out.println(data.get(0).city);
Then an error was arised:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.ssc.ctq.nav.util.MapData
Can anyone tell me why I get this error? Is implicit operator is not supported in TypeToken class?
you can do like this:
Gson gson = new Gson();
String jsonString = "[{\"id\":18,\"city\":\"test\",\"street\":\"test 1\",\"zipcode\":121209,\"state\":\"IL\",\"lat\":32.158138,\"lng\":34.807838},{\"id\":19,\"city\":\"test\",\"street\":\"1\",\"zipcode\":76812,\"state\":\"IL\",\"lat\":32.161041,\"lng\":34.810410}]";
List<Map> tmpList = gson.fromJson(jsonString);
List<T> resultList = new Arraylist<T>(tmplist.size());
for(Map map:tmpList){
String tmpJson = gson.toJson(map);
resultList.add(gson.fromJson(tmpJson, classT));
}
return resultList;
I met the same problem. From the Javadoc of TypeToken:
This syntax cannot be used to create type literals that have wildcard parameters, such as Class<?> or List<? extends CharSequence>.
You must explicitly indicate the type of T in TypeToken<T>, without generics.
You can use this method in order to parse generic json string to map
public Map<String, String> getMapFromJson(String jsonString) {
Map<String, String> map = new HashMap<>();
try {
JSONObject object = new JSONObject(jsonString);
Iterator<?> iterator = object.keys();
while (iterator.hasNext()) {
String key = (String) iterator.next();
if(!key.isEmpty() && !object.getString(key).isEmpty()){
map.put(key, object.getString(key));
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return map;
}