deserialization of a JSON API response with moshi - json

I got a null object attributes after deserialization of a json response.
Developing under android, I'm using retrofit2 , moshi as converter (https://github.com/kamikat/moshi-jsonapi ) .
When debugging ,I saw a json response fully retrieved (not null attributes),but deserialization fails. Should I use GSON instead?
Here's my retrofit builder I use to make my json call: (no issue)
public static JsonServerInterface getSimpleClient(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_AUTH_URL)a
.addConverterFactory(MoshiConverterFactory.create())
.build();
JsonServerInterface webServer=retrofit.create(JsonServerInterface.class);
return webServer;
}
My api json call,response contain UserModel with null attributes(deserialization fails without any error)
signInCall.enqueue(new Callback<UserModel>(){
#Override
public void onResponse
(Call<UserModel> call, Response<UserModel> response)
{
response.message();
}
}
My UserModel (as required by moshi ,but I think it lacks something):
#JsonApi(type = "users")
public class UserModel extends Resource {
#Json(name = "auth-token")
private String authToken;
#Json(name = "firstname")
private String firstname;
#Json(name = "lastname")
private String lastname;
#Json(name = "email")
private String email;
#Json(name = "created-at")
private String createdAt;
#Json(name = "updated-at")
private String updatedAt;
private HasMany<ActivityModel> activities;
My json response I saw when debugging http response, I retrieve without any trouve,but moshi sucks to deserialize it,and no errors are raised:
{
"data": {
"id": "21",
"type": "users",
"attributes": {
"auth-token": "t8S3BTqyPwN3T4QDMY1FwEMF",
"firstname": "aymen",
"lastname": "myself",
"email": "aymen.myself#gmail.com",
"created-at": "2017-11-13T22:52:39.477Z",
"updated-at": "2017-11-13T23:21:09.706Z"
},
"relationships": {
"activities": {
"data": [
{
"id": "81",
"type": "activities"
}
]
}
}
},
"included": [
{
"id": "81",
"type": "activities",
"attributes": {
"title": "activity 10",
"description": "how to draw a circle",
"start-at": "2017-11-13T23:06:13.474Z",
"duration": 10,
"created-at": "2017-11-13T23:06:32.630Z",
"updated-at": "2017-11-13T23:06:32.630Z"
},
"relationships": {
"user": {
"data": {
"id": "21",
"type": "users"
}
}
}
}
]
}

I find the solution after lot of hours:
I should use "Document" instead of UserModel
interface:
#POST("sign-in.json")
Call<Document> signIn(#Body Credentials credentials);
when calling:
signInCall.enqueue(new Callback<Document>(){
#Override
public void onResponse(Call<Document> call, Response<Document> response) {
hope it helps

Related

How to omit null field from Swagger/OpenAPI in ResponseEntity?

I'm trying to omit null values in my ResponseEntity.
My controller looks something like this:
#RestController
public class FooController {
//fields
//constructor
#PostMapping
public ResponseEntity<CreateFooResponseV10> createFoo(#Valid #RequestBody CreateFooRequestV10 foo, HttpServletRequest request) {
//some minor logic
return new ResponseEntity<>(aFooResponseV10Builder()
.withFirstName(foo.getFirstName())
.withLastName(foo.getLastName())
.withTestField(NULLABLE_OBJECT)
.build(), ...);
//I generated the builders from the output classes openapi-generator provided
}
// more stuff...
}
When NULLABLE_OBJECT is equal to null I expect the field to be omitted from the response like this:
{
"firstName": "John",
"lastName": "Doe"
}
But I either get these responses, depending on what I've tried so far:
{
"firstName": "John",
"lastName": "Doe",
"testField": null
}
or
{
"firstName": "John",
"lastName": "Doe",
"testField": {"present":false}
}
I generate my request/response objects (CreateFooResponseV10 and CreateFooRequestV10) with the use of openapi-generator
Here is my redacted api.json file:
{
"openapi": "3.0.1",
"info": { ... },
"servers": [ ... ],
"paths": {
"/foo": {
"post": {
...
"requestBody": {
"description": "Foo to be created",
"content": {
"application/foo+json;version=1.0": {
"schema": {
"$ref": "#/components/schemas/CreateFooRequest_V1_0"
}
}
},
"required": true
},
"responses": {
"201": {
"description": "Foo is successfully created",
"headers": { ... },
"content": {
"application/foo+json": {
"schema": {
"$ref": "#/components/schemas/CreateFooResponse_V1_0"
}
}
}
},
...
}
}
}
},
"components": {
"schemas": {
"CreateFooRequest_V1_0": {
"required": [
"firstName",
"lastName"
],
"type": "object",
"properties": {
"firstName": { ... },
"lastName": { ... },
"testField": {
"description": "...",
"type": "string",
"nullable": true
}
}
},
"CreateFooResponse_V1_0": {
"required": [
"firstName",
"lastName"
],
"type": "object",
"properties": {
"firstName": { ... },
"lastName": { ... },
"testField": {
"description": "...",
"type": "string",
"nullable": true
}
}
}
}
}
}
As you can see in both the request and response testField is not required and can be nullable.
So when testField is null it should be hidden from the response, but when it contains some date it should be shown of course.
I've tried overriding jackson's ObjectMapper bean as explained in this answer. Didn't work.
I've tried adding spring.jackson.default-property-inclusion=non_null to the application.properties. Didn't work.
What I think should work is adding #JsonIgnore above testField of the generated classes, but I don't know if this is something needed to be done manually (for each schema component, can be a lot of manual work for something that is generated) or if this can be configured in the plugin somewhere.
Thanks in advance.
extra info
OpenAPI 3.0.1
Maven 3.6.3
Java 11.0.2
jackson-databind-nullable 0.2.1
openapi-generator-maven-plugin 4.2.2
You can set the following in application.properties
spring.jackson.default-property-inclusion = NON_NULL
See Customize the Jackson ObjectMapper
Note: To make use of this, you need to #Autowire the ObjectMapper, and not manually create it
Try registering the following bean in your spring context. It should override default bean
#Bean
public HttpMessageConverters httpMessageConverters() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL)
return new HttpMessageConverters(
new MappingJackson2HttpMessageConverter(mapper));
}
You can generate the model classes with additional class annotations using OpenApi generator.
Just need to include this in your maven plugin:
<configOptions>
<additionalModelTypeAnnotations>
#com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
<additionalModelTypeAnnotations>
<configOptions>
see other config options here:
https://openapi-generator.tech/docs/generators/spring/
Try this code. I tested and it works.
#RestController
#RequestMapping("/testws")
public class TestWS {
#RequestMapping(value = "test", method = { RequestMethod.POST,
RequestMethod.GET }, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<TestBean> test(#Context HttpServletRequest request) {
TestBean testBean = new TestBean("John", "Doe", null);
return ResponseEntity.ok()
.body(testBean);
}
}
#JsonInclude(Include.NON_NULL)
class TestBean {
private String firstName;
private String lastName;
private String testField;
public TestBean() {
}
public TestBean(String firstName, String lastName, String testField) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.testField = testField;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getTestField() {
return testField;
}
public void setTestField(String testField) {
this.testField = testField;
}
}
Json response:
{"firstName":"John","lastName":"Doe"}

how to parse a json similar string and map it into a pojo

I am trying to map the following JSON to my POJO using Jackson. I have the following JSON and following POJOs. kindly let me know how to map the JSON to POJO.
JSON string :
{
"Application": {
"id": "0",
"name": "MyApp",
"users": [
{
"User": {
"id": "2",
"name": "Beth Jones"
}
}
],
"groups": [
{
"Group": {
"id": "1",
"name": "SimpleGroup",
"users": [
{
"User": {
"id": "2",
"name": "Beth Jones"
}
}
]
}
}
]
}
}
The POJO according to the client specification is below :
package com.example.custom;
//import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.example.Application;
import com.example.Group;
import com.example.User;
import java.util.Collection;
//#JsonIgnoreProperties(ignoreUnknown = true)
public class MyApplication extends Application {
private Collection<User> users;
private Collection<Group> groups;
public MyApplication(String id, String name) {
super(id, name);
}
public void setUsers(Collection<User> users) {
this.users = users;
}
public void setGroups(Collection<Group> groups) {
this.groups = groups;
}
#Override
public Collection<User> getUsers() {
return this.users;
}
#Override
public User getUser(String userId) {
for (User user: MyParser.myApp.getUsers()) {
if (user.getId().equals(userId))
return user;
}
return null;
}
#Override
public Collection<Group> getGroups() {
return this.groups;
}
#Override
public Group getGroup(String groupId) {
for (Group group: MyParser.myApp.getGroups()) {
if (group.getId().equals(groupId))
return group;
}
return null;
}
#Override
public String toString() {
return "MyApplication{" +
"users=" + users +
", groups=" + groups +
'}';
}
}
Mapping Logic :
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MyParser.myApp = mapper.readValue(rewriter.getText(),MyApplication.class);
The resulting object is not able to capture anything as it is all null. Kindly help. Thanks in advance.
I think you should model your JSON correctly, In the users list you shouldn't specify it again that the key is User, that should be preassumed that a list of users will only contain user, same goes for groups list.
IMHO the JSON should look something like this :
{
"application": {
"id": "0",
"name": "MyApp",
"users": [ . ==> Since this is a user List, it will definitely contains user.
{
"id": "2",
"name": "Beth Jones"
}
],
"groups": [
{
"id": "1",
"name": "SimpleGroup",
"users": [
{
"id": "2",
"name": "Beth Jones"
}
]
}
]
}
}
Now the POJO also needs some modification, I am just adding the bare-minimum POJO.
class Application { <====== Top Level Class
private Long id;
private String name;
private List<User> users; // Application has some Users
private List<Group> groups; // Application has some groups
}
class User {
private Long id;
private String name;
}
class Group {
private Long id;
private String name;
private List<User> users; // Each group has some associated users.
}
Now you can use any standard JSON library for Java and convert your JSON into POJO. This will simplify your structure and you won't face null issues with this structure.

How automatically parse response String into Map using RestTemplate

I'm using RestTemplate to retrieve list of issues from Jira. As response I get String with lots of fields, some of them are arrays. Request looks like:
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
Response string looks like:
{
"expand": "schema,names",
"total": 12,
"issues": [
{
"id": "32",
"key": "TEST-1",
"fields": {
"fixVersions": [
{
"description": "",
"releaseDate": "2017-04-02"
}
]
},
{
"id": "32",
"key": "TEST-2",
"fields": {
"fixVersions": [
{
"description": "",
"releaseDate": "2017-04-01"
}
]
}
]
}
Is it possible to convert this String into Map, where Object could be String or List of Map or something like this, without defining appropriate objects. As result, I'd like to have possibility to access description by: response.getIssues().get(0).getFields().getFixVersion().get(0).getDescription()
In such occasion, defining chain of specific objects looks too cumbersome.
You can create your own POJO classes which corresponds to the structure of the response JSON.
Based on the json that you have shared, you can have a class structure like this :
public class Response {
private String expand;
private String total;
private List<Issues> issues;
}
public class Issues {
private String id;
private String key;
private Map<String, List<FixVersions> fields;
}
public class FixVersions {
private String description;
private String releaseData;
}
Your GET call will change to the following :
ResponseEntity response = restTemplate.exchange(url,
HttpMethod.GET, entity, Response.class);
P.S. - All the fields in the POJO class must have their getters and
setters as well.

What is the convenient way to deserialize JSON(links + embedded container) using spring-hateoas?

colleagues!
We want to write Rest Client to service which follow the HATEOAS principle. So we have the following HAL+JSON representation and we want to deserialize it using spring-hateoas :
{
"id": "1",
"title": "album title",
"artistId": "1",
"stockLevel": 2,
"_links": {
"self": {"href": "http://localhost:8080/rest/albums/1"},
"artist": {"href": "http://localhost:8080/rest/artist/1"}
},
"_embedded": {
"albums": [{ //can be array or object
"id": "1",
"title": "album title",
"artistId": "1",
"stockLevel": 2,
"_links": {
"self": {"href": "http://localhost:8080/rest/albums/1"}
}
}],
"artist": { //can be array or object
"id": "1",
"name": "artist name",
"_links": {
"self": {"href": "http://localhost:8080/rest/artist/1"}
}
} //....
}
}
We expected the java object like this:
HalResource {
Resource<Album> //entity
List<Link> // _links
List<Resource<BaseEntity>>{ //_embedded
Resource<Album>
Resource<Artist>
....
}
}
So we have custom resource representation with embedded(list of resources) and entity(single resource):
#XmlRootElement(name = "resource")
public class HalResource<EntityType, EmbeddedType> extends Resources<EmbeddedType> {
#JsonUnwrapped
private EntityType entity;
public HalResource() {
}
public HalResource(Iterable<EmbeddedType> content, Link... links) {
super(content, links);
}
public EntityType getEntity() {
return entity;
}
public void setEntity(EntityType entity) {
this.entity = entity;
}
}
DTO classes:
public abstract class BaseEntity{}
#XmlRootElement(name = "album")
public class Album extends BaseEntity {
private String id;
private String title;
private String artistId;
private int stockLevel;
// getters and setters...
}
#XmlRootElement(name = "artist")
public class Artist extends BaseEntity {
private String id;
private String name;
// getters and setters...
}
And we want to get something like this, where Entity will be Artist or Album, but HalResourcesDeserializer return Resource.class with null content.
HalResource<Album, Resource<Entity>> resources =
restClient.getRootTarget().path("albums/1").queryParam("embedded", true).request().accept("application/hal+json")
.get(new GenericType<HalResource<Album, Resource<Entity>>>() {});
By using #JsonTypeInfo and #JsonSubTypes anotations we successfully deserialized our JSON(you can see the example on the github), but we don't want to have some additional type filds and anotattions in our DTO and JSON format.
We see one solution that is create a custom deserializer which can processing that.
So the question is: What is the convenient way to deserialize our JSON(links + embedded container) using spring-hateoas?
We use spring-hateoas 0.16v(but we tried 0.19v) and glassfish jersey 2.22.1
Thank you!

Parse a JSON to object JAVA without Root

The response of my service ALFRESCO REST is:
[
{
"role": "SiteManager",
"authority":
{
"authorityType": "USER",
"fullName": "admin",
"userName": "admin",
"firstName": "Administrator",
"lastName": "",
"url": "\/alfresco\/service\/api\/people\/admin"
},
"url": "\/alfresco\/service\/api\/sites\/test3\/memberships\/admin"
}
,
{
"role": "SiteConsumer",
"authority":
{
"authorityType": "GROUP",
"shortName": "jamalgg",
"fullName": "GROUP_jamalgg",
"displayName": "jamalgg",
"url": "\/alfresco\/service\/api\/groups\/jamalgg"
},
"url": "\/alfresco\/service\/api\/sites\/test3\/memberships\/GROUP_jamalgg"
}
,
{
"role": "SiteManager",
"authority":
{
"authorityType": "GROUP",
"shortName": "ALFRESCO_ADMINISTRATORS",
"fullName": "GROUP_ALFRESCO_ADMINISTRATORS",
"displayName": "ALFRESCO_ADMINISTRATORS",
"url": "\/alfresco\/service\/api\/groups\/ALFRESCO_ADMINISTRATORS"
},
"url": "\/alfresco\/service\/api\/sites\/test3\/memberships\/GROUP_ALFRESCO_ADMINISTRATORS"
}
]
And I want to parse to list of object:
List<Memberships > listMemberships;
public class Memberships {
private String role;
private List<Authority> listAuthority ;
private String url;
}
public class Authority {
private String authorityType;
private String shortName;
private String fullName;
private String displayName;
private String url;
}
I think that there are two solutions:
how to add the tag Memberships to JSON result for encapsulates
the whole.
how to parse JSON result directly to my list
Thanks
As answered in a-better-java-json-library I would use the google-gson library.
Thank you Ozoli. The answer to my question is:
Type targetType = new TypeToken<Collection<Memberships>>() {}.getType();
List<Memberships> list = (List<Memberships>) new Gson().fromJson(renduJson,targetType);
You can also use http://jsongen.byingtondesign.com/ to generate java code from json response and then use jackson library ( http://jackson.codehaus.org/ ) to bind that response data to your object(s):
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File("c:\\user.json"), User.class);
sorry for not formatting code
Type targetType = new TypeToken<Collection<Memberships>>() {}.getType();
List<Memberships> list = (List<Memberships>)new Gson().fromJson(rendu,targetType);