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: "..." },
...
]
}
Related
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 have JSON output correctly annotated and mapped with mixins for the User class with appropriate setters and getters for those properties:
public class User {
String first;
String middle;
String last;
...
}
When I use my Mixin:
public interface UserMixin {
#JsonProperty("first")
void setFirst(String first);
#JsonProperty("middle")
void setMiddle(String middle);
#JsonProperty("last")
void setLast(String last);
}
After registering the mixin and writing the User class using the ObjectMapper I get:
"User" :
{
"first" : "William",
"middle" : "S",
"last" : "Preston"
}
So to this point, for brevity, I lied a little bit - User as cited above is a large, legacy DTO class that is resistant towards modification.
And, while the mixin works great, our customer would rather see something like:
"User" :
{
"Name" :
{
"first" : "William",
"middle" : "S",
"last" : "Preston"
}
...
}
I repeat, the DTO is resistant to change. Ideally I'd refactor the DTO and do it correctly.
What I think I'm asking - is there some combination of Mixin/Annotation I can use to sub-class "Name" from already existing data in the User class? There's no Name subclass ... but all of the pieces necessary to "write out" the JSON in this format exist.
Lacking the existence of a #JsonWrapped annotation, my personal preferred solution here would be to use the converter functionality of #JsonSerialize (looks like you'd need Jackson 2.3+ for this; the annotation is supported in 2.2.2 but I got unexpected runtime errors).
Basically, a converter lets you do a pre-serialization transformation from one data structure to another. This lets you work with simple data classes rather than mucking about creating a custom serializer.
First, model your DTO how you want it to be serialized:
public static class UserDto {
private final Name name;
private UserDto(Name name) { this.name = name; }
public static UserDto fromUser(User user) {
return new UserDto(Name.fromUser(user));
}
public Name getName() { return name; }
public static class Name {
private final String first;
private final String middle;
private final String last;
private Name(String first, String middle, String last) {
this.first = first;
this.middle = middle;
this.last = last;
}
public static Name fromUser(User user) {
return new Name(user.getFirst(), user.getMiddle(), user.getLast());
}
public String getFirst() { return first; }
public String getMiddle() { return middle; }
public String getLast() { return last; }
}
}
Next, create a simple Converter class (I nested it in UserDto):
public static class Converter extends StdConverter<User, UserDto> {
#Override
public UserDto convert(User value) {
return UserDto.fromUser(value);
}
}
Then, use that converter class in your mixin:
#JsonSerialize(converter = UserDto.Converter.class)
public interface UserMixin {
}
I got json like below:
{"examinationTypes":[{"ExaminationTypeVO":{"id":1,"name":"Badanie krwi"}},{"ExaminationTypeVO":{"id":2,"name":"Spirometria"}},{"ExaminationTypeVO":{"id":3,"name":"Wymaz"}},{"ExaminationTypeVO":{"id":4,"name":"Ciśnienie"}},{"ExaminationTypeVO":{"id":5,"name":"EKG"}},{"ExaminationTypeVO":{"id":6,"name":"Elektrowstrząsy"}},{"ExaminationTypeVO":{"id":7,"name":"Tomografia"}},{"ExaminationTypeVO":{"id":8,"name":"Lewatywa"}},{"ExaminationTypeVO":{"id":9,"name":"Aneskopia"}},{"ExaminationTypeVO":{"id":10,"name":"Rektoskopia"}},{"ExaminationTypeVO":{"id":11,"name":"Kolonoskopioa"}},{"ExaminationTypeVO":{"id":12,"name":"Echo serca"}},{"ExaminationTypeVO":{"id":13,"name":"Ablacja"}},{"ExaminationTypeVO":{"id":14,"name":"Badnaie dopplerowskie"}},{"ExaminationTypeVO":{"id":15,"name":"Kapilaroskopia"}}]}
I have defined types:
#JsonRootName(value="ExaminationTypeVO")
#JsonIgnoreProperties(ignoreUnknown = true)
public class ExaminationTypeVO {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and
public class ExaminationTypesVO {
private List<ExaminationTypeVO> examinationTypes;
public List<ExaminationTypeVO> getExaminationTypes() {
return examinationTypes;
}
public void setExaminationTypes(List<ExaminationTypeVO> examinationTypes) {
this.examinationTypes = examinationTypes;
}
When I am deserializing it like that:
ExaminationTypesVO l = m.readValue(result, ExaminationTypesVO.class);
I receive an wrapper object but the list inside contains objects of type ExaminationTypeVO with all properties set to null.
Can anybody help to figure it out?
Your issue is that you have an extra level of object that you are trying to deserialize. Trying to not be confusing as I explain this: you have an array of objects, those objects contain a single ExaminationTypeVO object.
If you are stuck with the structure of the JSON that you provided, then you will need to add another "level" to your deserialization. You can do this via a wrapper object inside of your ExaminationTypesVO class:
public class ExaminationTypesVO {
private List<ExaminationTypeVOWrapper> examinationTypes;
public List<ExaminationTypeVOWrapper> getExaminationTypes() {
return examinationTypes;
}
public void setExaminationTypes(List<ExaminationTypeVOWrapper> examinationTypes) {
this.examinationTypes = examinationTypes;
}
public static class ExaminationTypeVOWrapper {
private final ExaminationTypeVO examinationTypeVO;
#JsonCreator
public ExaminationTypeVOWrapper(#JsonProperty("ExaminationTypeVO") ExaminationTypeVO examinationTypeVO) {
this.examinationTypeVO = examinationTypeVO;
}
public ExaminationTypeVO getExaminationTypeVO() {
return examinationTypeVO;
}
}
}
If you have control over the JSON that you are deserializing, you can just remove the extra "level" (ExaminationTypeVO wrapping object) and not have to change your code. Your new JSON in this approach would look like:
{
"examinationTypes": [
{
"id": 1,
"name": "Badanie krwi"
}, ...
]
}
With either of these approaches you can remove both of the class-level annotations you have on ExaminationTypeVO.
Let's say I have a Json object like this:
{
"name": "Bob Dole",
"company": "Bob Dole Industries",
"phone": {
"work": "123-456-7890",
"home": "234-567-8901",
"mobile": "345-678-9012"
}
}
And to help me read it, I use Jackson's Object Mapper with the following class:
public class Contact {
public static class Phone {
private String work;
private String home;
private String mobile;
public String getWork() { return work; }
public String getHome() { return home; }
public String getMobile() { return mobile; }
public void setWork(String s) { work = s; }
public void setHome(String s) { home = s; }
public void setMobile(String s) { mobile = s; }
}
private String name;
private String company;
private Phone phone;
public String getName() { return name; }
public String getCompany() { return company; }
public Phone getPhone() { return phone; }
public void setName(String s) { name = s; }
public void setCompany(String s) { company = s; }
public void setPhone(Phone p) { phone = p; }
}
My question is, how (using the simplest explanation possible), does the Object mapper "deserialize" the Json object? I thought it was matching variable names, but changing them by a few letters didn't affect the output. Then, I tried switching the order of the set() functions, but that didn't do anything. I also tried both, but that was also useless. I'm guessing there's something more sophisticated at work here, but what?
I tried to look in the documentation and past code, but I didn't see an explanation that made sense to me.
Without Annotations:
Without any annotations, it does what is called POJO mapping, it just uses reflection on the instance members and uses some rules about how to map the keys in the json to the names of the instance members. *note: it works on private members as well as public or package protected as well
If it doesn't match the names of the instance members, then it starts trying to match the getXXX and setXXX methods, if it doesn't match anything then it gives up.
With Annotations:
It uses the metadata supplied by the annotations to do the mapping and conversions.
It is always better to explicitly use the annotations when you have the source to add them to, then there is no guess work on what gets mapped to what.
Remember explicit is always better than implicit!
This is all well documented on the WIKI:
Mapping and Annotations
JSON Schema:
I am creating JSON Schema definitions for all my new projects now to document what is and isn't valid JSON according to the schema rules engine. It is a great way to document your data structures and eliminate parsing errors.
What should my POJO declaration be if I want to de-serialize the following JSON format:
{
"id":2,
"content":"skfdjhsfjhsfjkshdkjfhskdjf",
"created_at":"2012-11-14T00:10:59Z",
"feeds":
[
{
"feed":{"name":"twitter"}
}
]
}
Currently my POJOs look like that:
Broadcast.java - the outtermost object
public class Broadcast {
private String content;
private String created_at;
private Feeds feed[];
private int id;
public Broadcast() { }
public int getId() { return id; }
public String getContent() { return content; }
public String getCreatedAt() { return created_at; }
}
Feed.java - the inner child of Broadcast which can be an array and each feed has a name attribute:
public class Feeds {
private String name;
public Feeds() {}
public String getName() {
return name;
}
}
private Feeds feed[];
should be
private Feeds[] feeds;.
Actually, the placement of [] doesn't matter, but it's recommended to be placed alongside the type as it makes it clearer to read. The issue here is with the incorrect variable name.
Also, if you take a closer look at:
"feeds":
[
{
"feed":{"name":"twitter"}
}
]
you'll notice that feeds is indeed an array, but "feed":{"name":"twitter"} represents a variable (feed) holding a key-value pair ({"name":"twitter"}), thus your Feeds class should look like:
class Feeds {
private Map<String, String> feed;
public Feeds() {}
public String getName() {
return feed.get("name");
}
}