I need to create a model in Java to deseralize such JSON.
[
{
"yyy": {
"address": "y-a",
"acronym": "YYY"
},
"xxx": {
"address": "x-a",
"acronym": "XXX"
}
},
{
"vvv": {
"address": "v-a",
"acronym": "VVV"
}
}
]
It looks for me that a model in Java will look like here (i'm using Jackson)
public class Yyy{
public String address;
public String acronym;
}
public class Xxx{
public String address;
public String acronym;
}
public class Vvv{
public String address;
public String acronym;
}
public class Root{
public Yyy yyy;
public Xxx xxx;
public Vvv vvv;
}
It is not acceptable at all because I don't know which keys like xxx, yyy, vvv I will get -
They are not defined and can be random. What I need to do to convert this JSON to have something usefull? I mean to have an array of objects and in inside object a map where keys will be created from keys in JSON (vvv, yyy, xxx)
Related
I have a json response
"data": {
"students": [
{
"id": 100,
"name": "ABC"
},
{
"id": 101,
"name": "XYZ"
}
I need to map it to my pojo, something like -
public class TempClass {
List<Temp> list_students;
}
class Temp {
Long id;
String name;
}
Direct reading API response into my pojo gives me a class cast exception. I've tried converting response to a list of map and the collect as Temp class but that also doesn't work.
Exception -
java.util.LinkedHashMap cannot be cast to java object
Any suggestions please?
Code snippet for conversion -
new TempClass(((LinkedHashMap<String, Object>) response.getData()).entrySet())
.stream().map(map -> mapper.convertValue(map, Temp.class))
.collect(Collectors.toList()))
public class Data{
public ArrayList<Student> students;
}
public class Root{
public Data data;
}
public class Student{
public int id;
public String name;
}
Your POJO class will look like this
I have a Person class:
public class Person {
private String name;
private String address;
}
Now this Person class is used in another class as:
public class Group {
private Person nicePerson;
private Person badPerson;
private Person secretPerson;
}
Now, I want to serialize my Group object to JSON using Jackson ObjectMapper. I'm using a custom serializer based on some annotation.
When I serialize the Group object, I'm looking for the following output:
{
"nicePerson": {
"name": "John",
"address": "usa"
},
"badPerson": {
"name": "Harold",
"address": "canada"
},
"secretPerson": {
"name": "XXXXXXX",
"address": "london"
}
}
Here, I want to change the name of 'secretPerson' to "XXXXXXX".
But I'm having some problems.
I have made following annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD})
public #interface SecretInfo {
String maskedValue = "XXXXXXX";
String value() default maskedValue;
}
I have annotated my Person class following way:
public class Person {
#SecretInfo
private String name;
private String address;
}
I have made the following serializer which looks for this annotation and then changes the value. It is using some generics now for convenience.
public class SecretInfoSerializer<T> extends StdSerializer<T> implements ContextualSerializer {
private SecretInfo secretInfo;
public SecretInfoSerializer() {
super((Class<T>) String.class);
}
public SecretInfoSerializer(SecretInfo secretInfo,
JavaType clazz) {
super(clazz);
this.secretInfo = secretInfo;
}
#Override
public void serialize(T value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (secretInfo != null && value != null) {
jsonGenerator.writeString(secretInfo.value());
} else jsonGenerator.writeString(getValue(value));
}
#Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty property) {
if (property != null) {
return new SecretInfoSerializer<>(
property.getAnnotation(SecretInfo.class),
property.getType());
} else {
return new SecretInfoSerializer<>();
}
}
private String getValue(T value) {
if (value == null) {
return "null";
} else {
return value.toString();
}
}
}
I have associated this with Annotation Introspector and made the ObjectMapper.
Then I serialized my 'Group' class above. But I get the following output:
{
"nicePerson": {
"name": "XXXXXXX",
"address": "usa"
},
"badPerson": {
"name": "XXXXXXX",
"address": "canada"
},
"secretPerson": {
"name": "XXXXXXX",
"address": "london"
}
}
As you can see, since I annotated the 'name' field in Person class, I'm getting "XXXXXXX" for every Person class there is. But this is not what I wanted. I just want to change the value for 'secretPerson' only.
I don't know what to do:
Maybe there is some way such as following:
public class Group {
private Person nicePerson;
private Person badPerson;
#HasSecretInfo
private Person secretPerson;
}
Then the serializer would check if it #HasSecretInfo, only then look for #SecretInfo ...etc. something like this.
So I'm looking to solve this problem. If you have any solution, that'd be great.
I am getting the following error:
java.lang.IllegalStateException: Expected a string but was
BEGIN_OBJECT at line 1 column 29 path $.entree[0].photo
It makes no sense to me because my json response should be constructed as follows:
import com.google.gson.annotations.SerializedName;
public class Entree {
#SerializedName("id")
public int id;
#SerializedName("photo")
public Entree.Photo photo;
public class Photo {
#SerializedName("url")
public String url;
#SerializedName("web")
public Entree.Photo.Web web;
#SerializedName("mobile")
public Entree.Photo.Mobile mobile;
public class Web {
#SerializedName("url")
public String url;
}
public class Mobile {
#SerializedName("url")
public String url;
}
}
}
JSON data:
"entrees": [
{
"id": 32,
"photo":
{
"url": "4c312e9aed37a59319096a03_1.jpg",
"web": {
"url": "web_4c312e9aed37a59319096a03_1.jpg"
},
"mobile": {
"url": "mobile_4c312e9aed37a59319096a03_1.jpg"
}
}
},
...
Do you see how Photo is an object and not a string in my class? What am I doing wrong then?
Try mark the inner class with static
In Gson document , it says
Gson can also deserialize static nested classes. However, Gson can not
automatically deserialize the pure inner classes since their no-args
constructor also need a reference to the containing Object which is
not available at the time of deserialization. You can address this
problem by either making the inner class static or by providing a
custom InstanceCreator for it.
Actually, your
GSON Serializable class is not correct,
For a JSON response like this,
{
"entrees": [{
"id": 32,
"photo": {
"url": "4c312e9aed37a59319096a03_1.jpg",
"web": {
"url": "web_4c312e9aed37a59319096a03_1.jpg"
},
"mobile": {
"url": "mobile_4c312e9aed37a59319096a03_1.jpg"
}
}
}]
}
The GSON class will be as,
public class Entree {
#com.google.gson.annotations.SerializedName("entrees")
public List<Entrees> Entrees;
public static class Web {
#com.google.gson.annotations.SerializedName("url")
public String Url;
}
public static class Mobile {
#com.google.gson.annotations.SerializedName("url")
public String Url;
}
public static class Photo {
#com.google.gson.annotations.SerializedName("url")
public String Url;
#com.google.gson.annotations.SerializedName("web")
public Web Web;
#com.google.gson.annotations.SerializedName("mobile")
public Mobile Mobile;
}
public static class Entrees {
#com.google.gson.annotations.SerializedName("id")
public int Id;
#com.google.gson.annotations.SerializedName("photo")
public Photo Photo;
}
}
I'm trying to parse Json to Java by using Gson, but when I use fromJson(), I always get null. Who can explain this data structure for me? Thanks!
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://api.datamarket.azure.com/Data.ashx/Bing/SearchWeb/v1/Web?Query='bill'gates'&$skip=0&$top=1",
"type": "WebResult"
},
"ID": "9bd0942f-fe5b-44fc-8343-ef85e5b93a7e",
"Title": "The Official Site of Bill Gates - The Gates Notes",
"Description": "In the space between business and goverment, even a small investment can make a big impact on the lives of those in need.",
"DisplayUrl": "www.thegatesnotes.com",
"Url": "http://www.thegatesnotes.com/"
},
{
"__metadata": {
"uri": "https://api.datamarket.azure.com/Data.ashx/Bing/SearchWeb/v1/Web?Query='bill'gates'&$skip=1&$top=1",
"type": "WebResult"
},
"ID": "fdf0d3b9-b29f-43ef-b5ba-6bb4b1b04458",
"Title": "Bill Gates - Wikipedia, the free encyclopedia",
"Description": "William Henry \"Bill\" Gates III (born October 28, 1955) is an American business magnate and philanthropist. Gates is the former chief executive and current chairman of ...",
"DisplayUrl": "en.wikipedia.org/wiki/Bill_Gates",
"Url": "http://en.wikipedia.org/wiki/Bill_Gates"
}
],
"__next": "https://api.datamarket.azure.com/Data.ashx/Bing/SearchWeb/v1/Web?Query='bill'gates'&$skip=10&$top=10"
}
}
I think the data structure should be like this, but it doesn't work.
public class d {
public result[] results;
public String __next;}
public class result {
public information[] infolist;}
public class information {
public __metadata metadata;
public String ID;
public String Title;
public String Description;
public String DisplayUrl;
public String Url;}
public class __metadata {
public String uri;
public String type;}
Your Information class is the problem. Put the Information stuff into Result and remove the infolist from Result. Also, the field name for the meta data is __metadata. This isn't the class name. Lastly, you're missing a class to wrap d as a field.
public class DataContainer {
public Data d;
}
public class Data {
public Result[] results;
public String __next;
}
public class Result {
public Metadata __metadata;
public String ID;
public String Title;
public String Description;
public String DisplayUrl;
public String Url;
}
public class Metadata {
public String uri;
public String type;
}
You really should use common convention for class names. Gson won't preclude you from using your own names for classes. It only requires control for the name of the fields.
To deserialize:
String json = ... ;
DataContainer myDataContainer = new Gson().fromJson(JSONString , DataContainer.class);
Result[] myResult = myDataContainer.d.results;
Try that and see if that works.
Here's how you should interpret the JSON when you're writing a class structure around it for Gson:
An opening { indicates an object, so this will be a new class (or an existing one if they have the same fields)
A "this": indicates a field for the object it's inside, and the field must be named the same thing as the text in the string.
An opening [ indicates an array, a List, or a Set (Result[] results could just as easily be List<Result> results)
When the JSON is:
{
"result":200,
"data", "my data"
}
The definition of the class can be:
class HttpResult
{
#Key
public int result;
#Key
public String data;
}
When the JSON root is a array and has no Key:
[
{
"result:200",
"data":"data1"
},
{
"result":404,
"data":"data2"
}
]
Then the definition of the class is?
Same class definition, it's just a Collection of them instead of one.