I've been trying to improve my code on this but don't know how, the json i get is this:
{
"name": "Jhon",
"lastName": "Smith",
"clothes": {
"gender":"male",
"Shirt": {
"id": 113608,
"name": "Green Shirt",
"size": "Large"
},
"Pants": {
"id": 115801,
"name": "Black Leather Pants",
"size": "Large"
}
}
}
The way it works so far is having both Shirt and Pants classes but as they are identical im trying to use just one class that will get both of them.I have no say in how the json is generated so have to work with it as it comes.
This are my classes:
Class Person
public class Person{
private String lastName;
private String name;
private Clothes clothes;
}
Class Clothes
public class Clothes{
private Shirt Shirt;
private Pants pants;
private String gender;
}
Class Shirt
public class Shirt{
private String id;
private String name;
private String size;
}
Pants is the same as shirt, the thing is that i don't want my code to break if/when they decide to add another piece of clothing so i'm trying to do something like this
Class Clothes
public class Clothes{
private List<Something> clothList;
private String gender;
}
Instead of using 2 separate classes (Shirt and Pants) simply use one class - let's say Cloth. Tried it like that and worked fine:
Person class:
package com.dominikangerer.q29550820;
public class Person {
private String name;
private String lastName;
private Clothes clothes;
}
Clothes class:
package com.dominikangerer.q29550820;
public class Clothes {
private String gender;
private Cloth Shirt;
private Cloth Pants;
}
Cloth class:
package com.dominikangerer.q29550820;
public class Cloth {
private Integer id;
private String name;
private String size;
}
After Building these classes I tried it directly in a little main class which results in a perfectly fine deserialized.
Gson g = new Gson();
Person person = g.fromJson("{\"name\":\"Jhon\",\"lastName\":\"Smith\",\"clothes\":{\"gender\":\"male\",\"Shirt\":{\"id\":113608,\"name\":\"Green Shirt\",\"size\":\"Large\"},\"Pants\":{\"id\":115801,\"name\":\"Black Leather Pants\",\"size\":\"Large\"}}}", Person.class);
Didn't use #Expose here or #SerializedName("something") because it wasn't needed.
Hope it helps you out - otherwise please explain your problem in more detail and I will try to help you.
----------- Update ------------
Okay normally it's quite easy to cast a Json as you have it there in an normal Object - but the thing is inside the map (clothes) you have also a normal String value. For this purpose I would suggest you to enable the Gson functionality #Expose I will tell you why this would be a good idea later.
Let's start:
I removed the Clothes class with a Map<String, Object> which Gson can easily deserialize - problem here was that the we also have the gender inside that map. I modified the Person class which now works like this:
Person v2:
package com.dominikangerer.q29550820;
public class Person {
private String name;
private String lastName;
#SerializedName("clothes")
private Map<String, Object> clothesWrapper;
public String getGender() {
return clothesWrapper.get("gender").toString();
}
public void setGender(String gender) {
this.clothesWrapper.put("gender", gender);
}
}
Now we can already map the gender as String and modify it with the getter and setter - still have the map up there which contains Objects. Next thing we don't want the Map<String, Object> - for deserializing and serialization it's totally fine - but for working with the Cloths itself - it's stupid so let's get rid of it with the easiest way:
We modify our Person class like this:
Person v3:
package com.dominikangerer.q29550820;
public class Person {
#Expose
private String name;
#Expose
private String lastName;
#Expose
#SerializedName("clothes")
private Map<String, Object> clothesWrapper;
private Map<String, Cloth> clothes;
public String getGender() {
return clothesWrapper.get("gender").toString();
}
public void setGender(String gender) {
this.clothesWrapper.put("gender", gender);
}
public Map<String, Cloth> getClothes() {
if (clothes == null) {
Gson g = new Gson();
clothes = new HashMap<String, Cloth>();
for (Entry<String, Object> entry : clothesWrapper.entrySet()) {
if (entry.getKey().equals("gender")) {
continue;
}
String helper = g.toJson(entry.getValue());
Cloth cloth = g.fromJson(helper, Cloth.class);
clothes.put(entry.getKey(), cloth);
}
}
return clothes;
}
}
As you can see we now indirectly cast all the Clothes - we have to do this like that because it's the easiest way to get all the LinkedTreeMap to a Cloth-Object without running into a ClassCastException. As you can see we now have a Map<String,Object> clothesWrapper which let Gson Parse the object (can't find a better name - sorry) and the Map<String, Cloth> clothes map without Expose. Now you also need to setup the Gson with the enableExpose option which works like this:
(Using the Person v3 Class here - simply debug into it - works like a charm)
public class Main {
public static void main(String[] args) {
Gson g = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Person person = g.fromJson("{\"name\":\"Jhon\",\"lastName\":\"Smith\",\"clothes\":{\"gender\":\"male\",\"Shirt\":{\"id\":113608,\"name\":\"Green Shirt\",\"size\":\"Large\"},\"Pants\":{\"id\":115801,\"name\":\"Black Leather Pants\",\"size\":\"Large\"}}}", Person.class);
System.out.println(person.getClothes());
System.out.println(person.getGender());
}
}
You can find all classes in this github repository
Related
{
"car" : {
"color" : "red",
"year" : "2015",
"Activities" : [
{"activeDate" : "2019", "drivenBy" : "Jimmy"},
{"activeDate" : "2018", "drivenBy" : "Sam"}
],
"maker" : "GM"
}
}
I would like to write Java Class that represents the above JSON object to use it on my rest API request. I would appreciate your help.
Everything inside a pair of curly brackets can be seen as a java object. If you have square brackets then that means that you have a list and the simple properties are basic objects like strings, ints and so on.
So, for example:
{
"house" : {
"noRooms" : 3,
"rooms" : [
{"name" : "kitchen", "surface" : 10.5 },
{"name" : "bathroom", "surface" : 5 },
{"name" : "bedroom", "surface" : 12.3 }
]
}
}
In this, because you start with a pair of curly brackets you have your first object, which you can name whatever you want. For lack of a better name, I'll call mine Building.
Inside this building I have a property named house. Because this is specified by curly brackets it means we will need another object. We'll call this House.
Going deeper we see two properties of House: the number of rooms called noRooms and something called rooms which appears to be a list (notice the square brackets).
For the number of rooms, we don't need to create a different object as most frameworks/utilities know how to deal with this so, an integer will do just fine. For the things inside the list we will need special objects that we will call Room.
These objects have two properties, a name which translates into a string and a surface which could be a double.
So, your classes would look something like:
public class Building {
private House house;
//rest of the code
}
public class House {
private int noRooms;
private List<Room> rooms;
//rest of the code
}
public class Room {
private String name;
private double surface;
//rest of the code
}
Assuming that you're using Jackson as your serializer, you'll be needing three classes to perform the serialization/deserialization based on the example JSON object you provided:
public class ActivityResource {
private String activeDate;
public String getActiveDate() { return activeDate; }
public void setActiveDate(String activeDate) { this.activeDate = activeDate; }
private String drivenBy;
public String getDrivenBy() { return drivenBy; }
public void setDrivenBy(String drivenBy) { this.drivenBy = drivenBy; }
}
Followed by this:
public class CarResource {
private String color;
public String getColor() { return color; }
public void setColor(String color) { this.color = color; }
private String year;
public String getYear() { return year; }
public void setYear(String year) { this.year = year; }
#JsonProperty("Activities")
private List<ActivityResource> activities;
public List<ActivityResource> getActivities() { return activities; }
public void setActivities(List<ActivityResource> activities) { this.activities = activities; }
private String maker;
public String getMaker() { return maker; }
public void setMaker(String maker) { this.maker = maker; }
}
And finally this:
public class CarRequestResource {
private CarResource car;
public CarResource getCar() { return car; }
public void setCar(CarResource car) { this.car = car; }
}
With all these in place you'll be able to deserialize the request with no problems.
Based on the json you've posted, we're going to call the Java class Car.
Car has four attributes: color, year, activities, and maker.
You can simply find the datatype of each attribute by reviewing the json file. In this case, we have color, year and maker fields as strings and the activities is a list.
To define the list, it's better to create another class named "Activity" with two attributes: activeDate and driveBy (which are both strings)
public class Activity {
private String activityDate;
private String drivenBy;
...
}
Make sure to add constructors, getters and setters as well.
public class Car {
private String color;
private String year;
private List<Activity> activities;
private String maker;
...
}
There are several online tools to convert json files to Java classes, but I suggest you learn the basics and do it yourself in the future to make sure that you don't get stuck in the middle of a more complicated project.
Here is an interesting article helping you get familiar with it:
dzone.com:introduction-to-json-with-java
I have a class Person in gwt and I have sent an instance of Person with servlet converted using Gson from server to client. But in the client side seems I can't use Gson. From what I read in forums it seems that the best way is using AutoBeans to convert Json to object Person again.
However in AutoBeans I can only use an interface. I will appreciate if anyone can help me write it.
A json example I get from server and want to convert to Person class again:
{"name":"aaa","family":"fff","username":"uuu","age":20,"phones":[{"id":0,"phoneNumber":"0911111"}],"relatives":[null]}
public class Person implements Serializable {
private String name;
private String family;
private String username;
private int age;
private List<Phone> phones;
private List<Person> relatives;
public Person() {
}
public Person(String name, String family, String username, int age, List<Phone> phones, List<Person> relatives) {
this.name = name;
this.family = family;
this.username = username;
this.age = age;
this.phones = phones;
this.relatives = new ArrayList<Person>();
this.relatives = relatives;
}
public void addPhone(Phone p) {
phones.add(p);
}
public String getName() {
return this.name;
}
public String getFamily() {
return this.family;
}
public int getAge() {
return this.age;
}
public String getUsername() {
return this.username;
}
public List<Phone> getNumbers() {
return this.phones;
}
public List<Person> getRelatives() {
return this.relatives;
}
public String getAllNumbers() {
return Phone.convertPhonesToText(phones);
}
public static Person findPerson(List<Person> personList, String username) {
// .....
}
public static List<Person> convertTextToPersons(List<Person> personList, String personsText) {
// .....
}
public String convertPersonsToText() {
// ....
}
}
Yep, as commented by Tobika the other answer indicates that AutoBeans requires an Interface. AutoBeans feets better if you use it on both sides, client and server side and you define all your models as interfaces.
If you want to use your class models, you can use GWT Jackson which is pretty similar to AutoBeans but it uses your models, binding the json to your model (like other server side libraries; jackson, gson, etc):
https://github.com/nmorel/gwt-jackson
public static interface PersonMapper extends ObjectMapper<Person> {}
#Override public void onModuleLoad() {
PersonMapper mapper = GWT.create(PersonMapper.class);
String json = mapper.write(new Person("John", "Doe"));
GWT.log( json ); // > {"firstName":"John","lastName":"Doe"}
Person person = mapper.read(json);
GWT.log(person.getFirstName() + " " + person.getLastName());
}
Alternatively, you can use just plain GWT with JsInterop. This has many limitations but even with this limitation, it is a pretty good option. This is my favorite option if you can avoid inheritance in your DTOs. But this has the big advantage of being super lightweight (actually zero overhead mapping overhead and zero code overhead as it uses native parsing and no copies, accesing directly to the parsed json object). Limitations: cannot use inheritance, "broken type system" (all X instanceof SomeDtoType returns always true as all DTOs are of type Object wich makes sense because we are actually using the parsed JSON), cannot use collections only native arrays (but thanks to java8 Stream this should not be a problem, whatever you want to do with start with Stream.of(arr)), and only Double and Boolean boxed types supported (not supported any fancy type like Date or BigInteger, not supported long/Long...).
#JsType(isNative=true, package=GLOBAL, name="Object") final class Person {
// you can use getter/setter but as this class is final DTO adds no value
public String firstName; public String lastName; public Phome[] numbers;
// you can add some helper methods, don't forget to skip serialization!
public final #JsOverlay #JsonIgnore List<Phone> getNumberList() {
return Stream.of(numbers).collect(Collectors.toList());
}
}
#JsType(isNative=true, package=GLOBAL, name="Object) final class Phone {
public String number;
}
#JsMethod(namespace = "JSON") public static native <T> T parse(String text);
#Override public void onModuleLoad() {
Person person = parse("{\"firstName\":\"John\",\"lastName\":\"Doe\"}");
GWT.log(person.firstName + " " + person.lastName);
}
These simple and limited DTOs are more a DTO scheme than a type. But has a big advantage, this DTOs works out of the box with most of the server side parsers. Jackson and GSON will encode and parse without any configuration.
I try to use only immutables objects in my application. I've got a REST service that will take arbitrary JSon objects as input.
I've a Java class that map theses objects, and I want to make them immutable + able to deal with extra parameters (just like using #JsonAnySetter).
Here is my java class:
public class Operation {
private final String _id;
private final String state;
private final Map<String, Object> extra;
public Operation(String _id, String state, Map<String,Object> extra) {
this._id = _id;
this.state = state;
this.extra = extra;
}
// getters....
}
Using #JsonAnySetter I would have:
public class Operation {
private final String _id;
private final String state;
private Map<String, Object> extra = new HashMap<>();
public Operation(String _id, String state) {
this._id = _id;
this.state = state;
}
#JsonAnySetter
public void addExtra(String key, Object value) {
this.extra.put(key,value);
}
// getters....
}
But this is not immutable anymore !
This will not work because Jackson do not find any "extra" json attribute to read. I would like that everything that cannot be mapped be added to my map.
Any idea of how to do this ? (or is it just possible :)
Note: I use javac with -parameters option and the ParameterNameModule from jackson so that I don't need #JsonCreator option.
Ok so I respond to myself :)
It seems that it is not possible to do that using only Jackson.
Because I want immutability, I've turned myself to the 'immutables' framework: http://immutables.github.io/
With a little configuration, it will deal with extra parameters as stated in the following report: https://github.com/immutables/immutables/issues/185.
In my situation, I've got the following code:
#Value.Immutable
#JsonSerialize(as = ImmutableOperation.class)
#JsonDeserialize(as = ImmutableOperation.class)
public abstract class Operation {
#JsonAnyGetter
#Value.Parameter
public abstract Map<String, String> extra();
}
Refer to the documentation of immutables for the details.
If you want to deserialize immutable entity with extra arguments you can utilize builder pattern:
#JsonPOJOBuilder
public class OperationBuilder {
private String _id;
private String _state;
private Map<String, Object> extra = new HashMap<>();
#JsonAnySetter
public OperationBuilder addExtra(String key, Object value) {
this.extra.put(key,value);
return this;
}
// setters....
public Operation build() {
return new Operation(...arguments...)
}
And your original class should have this annotation on a class level:
#JsonDeserializer(builder = OperationBuilder.class)
This way all your known and unknown (extra) fields will be populated inside the builder and then Jackson will call build() method at the end of the deserialization.
I am completely new to Java but have experience in other languages. I have the following JSON file that I am trying to bring into a java program.
{
"Progression": [
{
"ProgressionName": "An Apple a Day...",
"Steps": [
{
"StepName": "Collect an Apple",
"Type": "COLLECT",
"RewardType": "UNLOCK_CRAFTING",
"Reward": "minecraft:golden_apple"
},
{
"StepName": "Craft a Golden Apple",
"Type": "CRAFT",
"RewardType": "GIVE",
"Reward": "minecraft:diamond"
}
]
},
{
"ProgressionName": "Keeps the Dr Away...",
"Steps": [
{
"StepName": "Collect an Apple",
"Type": "COLLECT",
"RewardType": "UNLOCK_CRAFTING",
"Reward": "minecraft:golden_apple"
},
{
"StepName": "Craft a Golden Apple",
"Type": "CRAFT",
"RewardType": "GIVE",
"Reward": "minecraft:diamond"
}
]
}
]
}
I have the following class that I thought was correct to store the JSON:
public class ProgressionData {
private Progression progresion;
public class Progression {
private String ProgressionName;
private ProgressionSteps Steps;
}
public class ProgressionSteps {
private String StepName;
private String Type;
private String RewardType;
private String Reward;
}
}
This is the call I am using:
BufferedReader br = new BufferedReader(new FileReader(configFile));
ProgressionData progressiondata = new Gson().fromJson(br, ProgressionData.class);
When I debug the code, progressiondata is NULL. I know I am missing something fundamental, and hoping someone can point me in the right direction.
you are trying to map an array of Progression objects to a Progression object,
change from
private Progression progresion;
to
private List<Progression> progresion;
or
private Progression[] progresion;
and the same thing for Progression Step objects
also, don't forget create getters/setters for your members or change access modifiers
Your class is wrong since you have JSONArray inside, please try the following:
public class ProgressionData {
private List<Progression> progresion;
public class Progression {
private String ProgressionName;
private List<ProgressionSteps> Steps;
}
public class ProgressionSteps {
private String StepName;
private String Type;
private String RewardType;
private String Reward;
}
}
Given your example, your base class should look something like the above:
public class ProgressionData {
private Progression[] progresion;
public class Progression {
private String ProgressionName;
private ProgressionSteps[] Steps;
// Add getters and setters
}
public class ProgressionSteps {
private String StepName;
private String Type;
private String RewardType;
private String Reward;
// Add getters and setters
}
// Add getter and setter for "Progression"
}
Notes:
progression and steps fields are actually arrays.
You need to add getters and setters methods for your properties.
You may experience some issues because you are parsing a Property (starting with upper-case P) key from your json to a property (lower-case p). If you try to change your field to Property, the compiler will probably complain because you can't have either a Class and a Field with the same name. In that case, you must look for some GSON parsing options (for ignoring case, for example).
You should update following things to make it work
There is a typo in progresion, it should be Progression. (This might be the primary reason for getting your object as Null, and Name of the variables should match with keys in Json, not classname. You can also use annotation in your POGO class to map the name of the variable with key using #SerializedName("Progression"))
Progression is an array
Steps inside Progression is an array
Please update your ProgressionData as below.
public class ProgressionData {
#SerializedName("Progression")
private List<Progression> progression;
public class Progression {
private String ProgressionName;
private List<ProgressionSteps> Steps;
//Setters and Getters
}
public class ProgressionSteps {
private String StepName;
private String Type;
private String RewardType;
private String Reward;
//Setters and Getters
}
//Setters and Getters
}
Use below code to parse your json,
BufferedReader br = new BufferedReader(new FileReader(configFile));
ProgressionData progressiondata = new Gson().fromJson(br, ProgressionData.class);
I will be recieving JSON strings in the following format:
{ "type":"groups", "groups":[ {"group":"NAME"}, ...] }
How would one form an object so that the following would work.
MyClass p = gson.fromJson(jsonString, MyClass.class);
The part I'm stuck it is "{"group":"NAME"}". Would this be fixed by saving objects inside the an array? Example.
public class MyClass {
private String type;
private List<MyOtherClass> groups = new ArrayList<MyOtherClass>();
//getter and setter methods
}
Answer: Nesting objects in each other doh! Thanks you guys! Crystal clear now :D
public class MyOtherClass {
private String group;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
#Override
public String toString() {
return "group: "+group;
}
}
First you need a POJO for the group:
public class MyOtherClass {
#Expose
private String group;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
}
Next you need one for your 'MyClass', which would look like this:
public class MyClass {
#Expose
private String type;
#Expose
private List<MyOtherClass> groups = new ArrayList<MyOtherClass>();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Group> getGroups() {
return groups;
}
public void setGroups(List<Group> groups) {
this.groups = groups;
}
}
Hope this helps.
At first glance, this looks fine, assuming MyOtherClass has a field called group that holds a String. What do you mean by "the part I'm stuck [on]"? Perhaps you could post the stack trace you're seeing, a broader description of what you're trying to do, or best of all a SSCCE?
When using GSON, I find it easiest to implement the class structure I need, then let GSON generate the JSON data from that. You certainly can go the other way (design class structure based on JSON data), but I think it's more confusing if you don't understand what GSON is trying to do.
Some pseduo-code:
Class MyClass
String type
List<MyOtherClass> groups
Class MyOtherClass
String group
Looking at this we can easily see the JSON that will hold our serialized object will look like so:
{
type: "...",
groups: [
{ group: "..." },
...
]
}