I have json data like this:
meds:
[
{
MedsID: 8063,
updated_at: "2015-11-04T06:59:55",
treatment_date: "2015-11-04T00:00:00",
name: "name"
}
],
scores:
[
{
ScoreID: 75820,
updated_at: "2015-11-04T06:59:55"
dialysis_flow: 233,
}
],
dias:
[
{
DiasID: 75820,
updated_at: "2015-11-04T06:59:55"
name: "K",
}
]
And here is my Entities:
public class BaseData{
public long id;
}
public class Med extends BaseData{
public String name;
public String updated_at;
public String treatment_date;
}
public class Score extends BaseData{
public String updated_at;
public int dialysis_flow;
}
public class Dias extends BaseData{
public String name;
public String updated_at;
public String treatment_date;
}
Because all entities are mapped from database with the id key (as I use orm db, it's loaded by property name ). So I need to parse all other keys MedsID, DiasID, ScoreID into id when mapping by gson. Is there any way to achieve that?
Update:
I use registerTypeHierarchyAdapter instead of registerTypeAdapter and it can work. But this way is extremely slow as my json data is very large.
public class DataDeserializer implements JsonDeserializer<BaseData> {
#Override
public BaseData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject ja = json.getAsJsonObject();
Gson gson = new Gson();
final String[] mapIds = {"ScoreID", "MedsID", "DiasID"};
BaseData data = gson.fromJson(ja, typeOfT);
for (String idKey:mapIds){
if(ja.has(idKey)){
data.id = ja.get(idKey).getAsLong();
break;
}
}
return data;
}
}
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter( BaseData.class, new DataDeserializer() ).create();
Does anyone know other way to achieve that?
The only way to achieve this is writing a custom de-serializer. Please see below example:
public class CustomDeserializer implements JsonDeserializer<Dias>{
public Dias deserialize( JsonElement json, Type typeOfT, JsonDeserializationContext context ) throws JsonParseException{
JsonObject ja = json.getAsJsonObject();
Dias dias = new Gson().fromJson( ja, Dias.class );
dias.id = ja.get( "DiasID" ).getAsLong();
return dias;
}
}
Register it;
String dias = "{'DiasID':75820,'updated_at':'2015-11-04T06:59:55','name':'K'}";
Gson gson = new GsonBuilder().registerTypeAdapter( Dias.class, new CustomDeserializer() ).create();
Dias dias2 = gson.fromJson( dias, Dias.class );
System.out.println( dias2.id );
Output:
75820
This is just an example, you can extend it by writing a deserializer for your own base class.
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 use Google Room Persistence Library for saving data in database. in Room there is an annotation (#Embedded) :
you can use the #Embedded annotation to represent an object that you'd like to decompose into its subfields within a table. You can then query the embedded fields just as you would for other individual columns
#Entity
public class MyObject {
// nested class
public class GeneralInfo {
public String ownerName;
#PrimaryKey
public long wellId;
}
#Embedded
public GeneralInfo generalInfo;
public long objectId;
// other fields
}
I use Gson to deserialize json string from REST API, I want Gson to deserialize GeneralInfo fields into MyObject fields directly. How can I do this?
I want Gson to deserialize MyObjects like this:
{
objectId : 1
wellId : 1
ownerName : "Me"
}
NOT this
{
generalInfo : {
wellId : 1
ownerName : "Me"
}
objectId : 1
}
Is there any way other than using JsonAdapter ? I can write my own convertToJson and convertFromJson but I want to use Gson and it's better to use annotation to tell Gson "don't deserialize this embeddede object to a jsonObject, insert its field in its parent json fields"
You can use a custom TypeAdaptor:
public static void main(String[] args) {
final MyObject myObject = new MyObject("Fardwark", 123, 9999L);
final String json = gson.toJson(myObject);
System.out.println(json);
final MyObject myObject2 = gson.fromJson(json, MyObject.class);
final String json2 = gson.toJson(myObject2);
System.out.println(json2);
}
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(MyObject.class, new MyTypeAdapter())
.create();
private static final class MyTypeAdapter extends TypeAdapter<MyObject> {
#Override
public void write(JsonWriter jsonWriter, MyObject myObject) throws IOException {
jsonWriter.beginObject()
.name("ownerName").value(myObject.generalInfo.ownerName)
.name("wellId").value(myObject.generalInfo.wellId)
.name("objectId").value(myObject.objectId)
.endObject();
}
#Override
public MyObject read(JsonReader jsonReader) throws IOException {
String ownerName = null;
int wellId = 0;
long objectId = 0;
jsonReader.beginObject();;
for (int i = 0; i < 3; ++i) {
switch (jsonReader.nextName()) {
case "ownerName":
ownerName = jsonReader.nextString();
break;
case "wellId":
wellId = jsonReader.nextInt();
break;
case "objectId":
objectId = jsonReader.nextLong();
break;
}
}
jsonReader.endObject();
return new MyObject(ownerName, wellId, objectId);
}
}
Output:
{"ownerName":"Fardwark","wellId":123,"objectId":9999}
{"ownerName":"Fardwark","wellId":123,"objectId":9999}
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);
Json string:
[
//Object 1
{
TypeName:"CheckSpecificDday",
SpecificDay:"20160413",
Lunar:1
},
{
TypeName:"CheckSpecificDday",
SpecificDay:"20160414",
Lunar:1
},
//Object 2
{
TypeName:"CheckEveryDayDday",
StartDate:"20160413",
EndDate:"20260417",
Interval:1,
StartOption:"D",
HolidayCondition:1
},
//Object 3
{
TypeName:"CheckEveryDdayOfWeek",
StartDate:"20160413",
EndDate:"",
Interval:1,
SpecificDayOfWeek:"3",
HolidayCondition:1
},
//Object 4
{
TypeName:"CheckEveryMonthSpecificDday",
StartDate:"20160413",
EndDate:"",
Interval:1,
SpecificDD:"13,14",
HolidayCondition:1
},
//Object 5
{
TypeName:"CheckEveryYearWeek",
StartDate:"20160413",
EndDate:"",
Interval:1,
SpecificMMnthWeek:"0433",
HolidayCondition:1
}
]
I have a Json array like the above. What I want is to parse it to different object types with Gson (as I commented to make it clearer), but I dont know how to do that. Please help me. Thank you in advance!
I think there are lots of simmilar questions on SO. One, Two
One way to parse this is to use simple
Object[] result = new Gson().fromJson(json, Object[].class);
But this will give you objects of LinkedTreeMap<Integer, LinkedTreeMap<String, String>> or something like this. You can use it, but its kinda hard and you will also have problems with your integers comming as doubles.
The other approach is to create custom interface or abstract class with TypeName field if you need it:
private interface CheckInterface{}
and implement it with every POJO classes of object types you have:
private static class CheckEveryDayBase implements CheckInterface{
private String StartDate;
private String EndDate;
private int Interval;
private int HolidayCondition;
}
private static class CheckSpecificDday implements CheckInterface{
private String SpecificDay;
private int Lunar;
}
private static class CheckEveryDayDday extends CheckEveryDayBase{
private String StartOption;
}
private static class CheckEveryDdayOfWeek extends CheckEveryDayBase{
private String SpecificDayOfWeek;
}
private static class CheckEveryMonthSpecificDday extends CheckEveryDayBase{
private String SpecificDD;
}
private static class CheckEveryYearWeek extends CheckEveryDayBase{
private String SpecificMMnthWeek;
}
Then create custom desrializer for your CheckInterface:
public static class CheckInterfaceDeserializer implements JsonDeserializer<CheckInterface>{
#Override
public CheckInterface deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
JsonObject jObject = (JsonObject) json;
JsonElement typeObj = jObject.get("TypeName");
if(typeObj!= null ){
String typeVal = typeObj.getAsString();
switch (typeVal){
case "CheckSpecificDday":
return context.deserialize(json, CheckSpecificDday.class);
case "CheckEveryDayDday":
return context.deserialize(json, CheckEveryDayDday.class);
case "CheckEveryDdayOfWeek":
return context.deserialize(json, CheckEveryDdayOfWeek.class);
case "CheckEveryMonthSpecificDday":
return context.deserialize(json, CheckEveryMonthSpecificDday.class);
case "CheckEveryYearWeek":
return context.deserialize(json, CheckEveryYearWeek.class);
}
}
return null;
}
}
Here is how you can use this:
GsonBuilder builder = new GsonBuilder();
// Register custom deserializer for CheckInterface.class
builder.registerTypeAdapter(CheckInterface.class, new CheckInterfaceDeserializer());
Gson gson = builder.create();
CheckInterface[] result2 = gson.fromJson(json, CheckInterface[].class);
I have a structure that I want to store using JSON in a file. None of the implementation classes will have more significant information than what is given.
public class ItemExample implements IItem{
private ModelMap map;
private String name;
}
public class ModelMap {
private HashMap<Coord, IPartType> map;
}
public class Coord {
private int x,y,z;
}
public class PartExample implements IPartType {
private String name;
private Purity purity;
}
public Enum Purity{
}
I am brand new to creating JSONs, I've been reading up on how Gson works but I am not really understanding how to translate the examples to my case. Most examples assume knowledge of certain aspects that I just don't know yet.
This is what I have started to do for IPartType:
public class PartDeserialize<T> implements JsonDeserializer<T>{
#Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject content = json.getAsJsonObject();
String name = content.get("name").getAsString();
Purity purity = Purity.valueOf(content.get("purity").getAsString());
return new Gson().fromJson(content, typeOfT);
}
}
I would appreciate any help.
Possible JSON Example per request:
{
"name" : "sword",
"map" :
{
"map" :
{
"1,1,1" : //string representation of Coord
{
"name" : "blade",
"purity" : "base" // string representation of Purity Enum
},
"0,0,0" :
{
"name" : "handle",
"purity" : "high"
}
}
}
}