Render complex object in JSON in grails - json

I have complex object and I want to render it, but I have several problems in view.
First of all, I have UUID field in my class but in View I get not the String but mostSigBits and leastSigBits.
The second one, I have my enums fields like two fields with enum and value
For example,
public class ExampleObject {
#JsonProperty("id")
private UUID id;
#JsonProperty("name")
private String name;
#JsonProperty("address")
private String address;
#JsonProperty("port")
private String port;
#JsonProperty("users")
#Valid
private List<UserRef> users = null;
#JsonProperty("indexingParameters")
private IndexingParameters indexingParameters;
#JsonProperty("otherParameters")
private OtherParameters otherParameters;
#JsonProperty("status")
private Status status;
}
When I get response from controller I get answer with this one
{
"id": {
"leastSignificantBits": -5406850341911646206,
"mostSignificantBits": 8884977146336383467
},
"status": {
"enumType": "api.model.Status",
"name": "GENERAL"
}
....
}
The problem is I have a lot of different but with the same problem objects in my code. If there is only 1 object, I`d easy prepare some _exampleObject.gson template and render every answer from controller to it, but I have many objects.
I think there are some variants to render correct my JSON, isn`t there?
Another rendering variants where data is ExampleObject.class or something like that
1)code:
Map map = [content: data.content, sorting: data.sorting, paging: data.paging] as Map
render AppResponse.success([success: true, data: map]).asJSON()
render data as JSON
on front:
Incorrect UUID and DateTime convert each field in Object, But I need Timeshtamp
"id": {"leastSignificantBits": -5005002633583312101,
"mostSignificantBits": 4056748206401340307},
"key": "b48d35dd-0551-4265-a1b1-65105e713811",
2)code:
Map map = [data: new ObjectMapper().writeValueAsString(data)] as Map
render map
on front:
Here we can see square brackets at the start which is wrong for JSON
['data':'{"content":[{"id":"384c7700-09c1-4393-ba8a-a89f555f431b","name":"somename"...
3)code:
Object result = new HashMap<String, Object>()
result.success = true
result["data1"] = new ObjectMapper().writeValueAsString(data)
render result as JSON
on front:
Here we can see quotes escaping
"data": "{\"content\":[{\"id\":\"384c7700-09c1-4393-ba8a-a89f555f431b\",\"name\":\"somename\",\"key\":\"b48d35dd-0551-4265-a1b1-65105e713811\",\"status\":\"COMPLETED\.......

I did it like this
#CompileStatic
class MyProxyController {
#Autowired
Myservice service
static ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JodaModule())
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
def getExampleObject {
ExampleObject exampleObject = service.getExampleObject()
render([contentType: "application/json"], objectMapper.writeValueAsString(new CustomObject(data, true)))
}
#CompileStatic
class CustomObject {
Boolean success
Object data
CustomObject(Object data, Boolean success) {
this.data = data
this.success = success
}
}
}
And get json as I wanted like
{
"success": true,
"data": {
"content": [
{ ....

Related

Moshi Custom JsonAdapter

I am trying to create a custom JsonAdapter for my JSON data that would bypass the serialization of specific field. Following is my sample JSON:
{
"playlistid": 1,
"playlistrows": [
{
"rowid": 1,
"data": {
"123": "title",
"124": "audio_link"
}
}
]
}
The JSON field data in above have dynamic key numbers, so I want to bypass this data field value and return JSONObject.
I am using RxAndroid, Retrofit2 with Observables. I have created a service class:
public static <S> S createPlaylistService(Class<S> serviceClass) {
Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(baseURL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClientBuilder.build())
.addConverterFactory(MoshiConverterFactory.create());
return builder.build().create(serviceClass);
}
I am calling this service using observable like this:
#GET("http://www.mylink.com/wp-json/subgroup/{subgroupId}/playlist/{comboItemId}")
Observable<Playlist> getPlaylist(#Path("subgroupId") int subgroupId, #Path("comboItemId") int comboItemId);
Then I run it like this:
ServiceBuilder.createPlaylistService(FHService.class).getPlaylist(123, 33);
My Pojo classes look like this:
public class Playlist {
#Json(name = "playlistid")
public Long playlistid;
#Json(name = "playlistrows")
public List<Playlistrow> playlistrows = null;
}
public class Playlistrow {
#Json(name = "rowid")
public Long rowid;
#Json(name = "data")
public Object data;
}
The problem is it would return a data value in this format:
{
123=title,
124=audio_link
}
which is invalid to parse as JSONObject.
I have Googled a lot and have also checked some Moshi example recipes but I had got no idea about how to bypass this specific field and return valid JSONObject, since I am new to this Moshi library.

type handling by Jackson/Spring conversion of JSON to java POJO

I'm using
Spring 3.1.0.RELEASE
Jackson 1.9.5
I'm using org.springframework.web.client.RestTemplate's getForObject() method:
getForObject(String url, Class<?> responseType, Map<String, ?> urlVariables) throws RestClientException
Here's my JSON:
{
"someObject": {
"someKey": 42,
},
"key2": "valueA"
}
Here's the POJO used to hold it:
SomeClass.java:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"someObject",
"key2"
})
public class SomeClass {
#JsonProperty("someObject")
private SomeObject someObject;
#JsonProperty("key2")
private String key2;
#JsonProperty("someObject")
public LocationInfo getSomeObject() {
return someObject;
}
#JsonProperty("someObject")
public void setLocationInfo(SomeObject someObject) {
this.someObject = someObject;
}
}
SomeObject.java:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"someKey"
})
public class SomeObject{
#JsonProperty("someKey")
private String someKey;
#JsonProperty("someKey")
public String getSomeKey() {
if(someKey==null){
someKey = "";
}
return someKey.toUpperCase();
}
#JsonProperty("someKey")
public void setSomeKey(String someKey) {
this.someKey = someKey;
}
}
It works. Given the JSON structure, I get a String value of "42" in the property someKey of class SomeObject
I don't understand why. Is there some magical conversion going on behind the scenes of which I'm unaware?
Can the conversion be counted on? Also, i'm not currently getting any whitespace at the beginning or end of the String someKey. Is that something I can count on as well, since the integer value cannot have any whitespace?
Check out the code at https://github.com/joelittlejohn/jsonschema2pojo if you want to really understand how it works.
Yes the conversion can be counted on, yes you can count on their not being whitespace in the String in the pojo.
In a nutshell the fields from the JSON file are read in, then these get mapped to the member variables/setter methods of the Pojos that is passed in as your responseType.

Format JSON Resulting expression

I'm writing a REST api using Jersey2 and Spring and I would like to format the resulting JSON expression to something more specifique and I don't know if I'm supposed to modify structure of my POJO or format the response on the ressource
Actual JSON
Response [ {
"rcId" : 22900,
"posId" : 595,
"status" : "PERC6",
"dateFrom" : 1438380000000,
"dateTo" : 1442095200000,
"creaDate" : 1442349754000
"createdBy": "52e28419-2c48-526d-8e7c-783cf331e071",
"modifiedBy": "52e28419-1725-84bd-9884-6969e7b9b876",
} ]
Wanted formated JSON
Response [ {
“results”: {
"rcId" : 22900,
"posId" : 595,
"status" : "PERC6",
"dateFrom" : 1438380000000,
"dateTo" : 1442095200000,
"creaDate" : 1442349754000
"createdBy": "52e28419-2c48-526d-8e7c-783cf331e071",
"modifiedBy": "52e28419-1725-84bd-9884-6969e7b9b876",
}
"related": {
"52e28419-2c48-526d-8e7c-783cf331e071": { "user/username" : "test" }
"52e28419-1725-84bd-9884-6969e7b9b876": { “user/username” : “test” }
}
"errors": [ ... If errors while executing query... ]
}
My object looks like this
#Entity
#Table(name="STATUS")
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class RcPosStatus implements Serializable {
private static final long serialVersionUID = -8039686696076337853L;
#Id
#Column(name="RC_ID")
#XmlElement(name = "RC_ID")
private Long rcId;
#Column(name="POS_ID")
#XmlElement(name = "POS_ID")
private Long posId;
#Column(name="STATUS")
#XmlElement(name = "STATUS")
private String status;
#Column(name="DATE_FROM")
#XmlElement(name = "DATE_FROM")
private Date dateFrom;
#Column(name="DATE_TO")
#XmlElement(name = "DATE_TO")
private Date dateTo;
#Column(name="CREA_DATE")
#XmlElement(name = "CREATION_DATE")
private Date creaDate;
My resource
#GET
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<RcPosStatus> getRcPosStatus(
#QueryParam("orderByInsertionDate") String orderByInsertionDate,
#QueryParam("numberDaysToLookBack") Integer numberDaysToLookBack)
throws IOException, AppException {
List<RcPosStatus> status = statusService.getRcPosStatus(
orderByInsertionDate, numberDaysToLookBack);
return status;
}
I recommend you create DTOs (Data Transfer Objects). You should not change the data model to represent the outcome of the REST endpoint. The reason for this is because requirements in the clients change over time and it's important to have a solid model that is stable.
It is a matter of architecture how you want to organize the DTOs, here is an example if I understand your model correct:
public class DefaultResponseDTO<Foo, Bar> implements Serializable {
private ArrayList<Foo> results;
private ArrayList<Bar> related;
private ArrayList<Errors> errors;
...
}
Now using it to create the response you want
... //fetches data from resources and starting to map response.
DefaultResponseDTO<Pojo, Pojo> response = new DefaultResponseDTO();
response.results = results; //some results you want to return, Entities
response.related = releated; //some results you want to return as related, Entities
response.errors = errors;
return response;
Using generics lets you create a default response that suits your needs, and helps you to keep a clean and neat API. Here is a list object I like to use, that has meta data about the list that I use in the clients for paging: http://pastebin.com/mTU4qbc7

Save object name into a instance variable during GSON Deserialization

Suppose I have a json file like this:
{
"ObjectName1": {
"enabled": true,
"SSOIDS": []
},
"ObjectName2": {
"enabled": true,
"SSOIDS": []
},
"ObjectName3": {
"enabled": true,
"SSOIDS": []
},
"ObjectName4": {
"enabled": true,
"IDs": []
}
}
I want to derserialize the data and store the "ObjectNameX" into a field, objectName, of my Java object e.g:
public class Feature implements Serializable {
private static final long serialVersionUID = 1L;
private String objectName;
private Boolean enabled;
private List<String> IDs;
private boolean checkLastTwoChars; //sometimes my json objects might have this
//element.However in this example it doesn't
//Getters and Setters left out for brevity
I have read a bit on creating a custom deserializer here
and have created the below class:
public class FeatureDeserializer implements JsonDeserializer<Feature> {
public Feature deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Feature ft = new Feature();
if(!json.isJsonNull()){
ft.setFeatureName(json.getAsJsonObject().getAsString());
//json.getAsJsonObject().getAsString()--> `{"enabled":true,"SSOIDS":[],"checkLastTwoChars":false}
}
return ft;
}
}
but the json parameter in the deserializer doesn't have access to the objectNameX during runtime i.e only the key value pairs of the objet fields are available.
I know GSON is deserializing the correct values and has access to the a objectNameX from eclipse debugger.
Here's how I am calling the fromJson():
// just the part I think is relevant
Map<String, Feature> featureCache = new HashMap<String, Feature>();
for(File file : files){
try {
BufferedReader br = new BufferedReader(new FileReader(file));
StringBuilder sb = new StringBuilder();
while(br.ready()){
sb.append(br.readLine());
}
br.close();
Gson gson = new GsonBuilder().setPrettyPrinting().
registerTypeAdapter(Feature.class, new FeatureDeserializer()).create();
featureCache = gson.fromJson(sb.toString(), new TypeToken<SortedMap<String, Feature>>(){}.getType()); // features in a specific file
Is there a standard way for saving each objectNamex in each unique object that I deserialize??
Here's my solution to the problem of saving each objectNamex in each unique object that I deserialize however I am still unsure if this is a best or even common practice.
I realized that:
Gson gson = new GsonBuilder().setPrettyPrinting().
registerTypeAdapter(Feature.class, new FeatureDeserializer()).create();
featureCache = gson.fromJson(sb.toString(), new TypeToken<SortedMap<String, Feature>>(){}.getType()); // features in a specific file
is actually creating a map with the objectNamex as the key to each unique Feature object therefore
I created a helper function:
private void fillInFeatureNames(Map<String, Feature> featureCache){
for (String featName: featureCache.keySet()){
featureCache.get(featName).setFeatureName(featName);
}
}
that cycles through each key in the map and sets each unique feature object featureName field to the keyname. This is a valid work around but I still wanted to know if there was a preferred practice in this effort.

FlexJson deserialize object reference

I'm using Spring Roo which generated set of hibernate and FlexJSON classes.
I have entity called Location and entity called Comment.
Location has many comments (1:M).
I'm trying to generate JSON object, which will, when deserialized and inserted reference existing Location object.
When I omit location field, everything is working fine, for example:
{
"date": 1315918228639,
"comment": "Bosnia is very nice country"
}
I don't know how to reference location field.
I've tried following, but with little success:
{
"location": 10,
"date": 1315918228639,
"comment": "Bosnia is very nice country"
}
where location id is 10.
How can I reference location field in the JSON?
Edit: Added Comment entity:
#RooJavaBean
#RooToString
#RooJson
#RooEntity
public class Komentar {
private String comment;
#ManyToOne
private Location location;
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(style = "M-")
private Date date;
}
I've solved issue by adding transient property.
#Transient
public long getLocationId(){
if(location!=null)
return location.getId();
else
return -1;
}
#Transient
public void setLocationId(long id){
location = Location.findLocation(id);
}
Got similar problem, but i can't change incoming json message, so i've changed generated aspect file:
#RequestMapping(value = "/jsonArray", method = RequestMethod.POST, headers = "Accept=application/json")
public ResponseEntity<String> Komentar.createFromJsonArray(#RequestBody String json) {
for (Komentar komentar: Komentar.fromJsonArrayToProducts(json)) {
komentar.setLocation(Location.findLocation(komentar.getLocation().getId()));
komentar.persist();
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
return new ResponseEntity<String>(headers, HttpStatus.CREATED);
}
komentar.setLocation(Location.findLocation(komentar.getLocation().getId())); was added by me.
I got same problem and solved it by introducing a custom object factory.
Since JSONDeserializer expect a json object for location attribute (ex:"Location":{"id":10,..}), supplying location id as a String/Integer (ex:"Location":"10") will give you an exception.
Therefore I have written LocationObjectFactory class and telling flexjson how to deserialize a Location class object in the way I want.
public class LocationObjectFactory implements ObjectFactory {
#Override
public Object instantiate(ObjectBinder context, Object value,
Type targetType, Class targetClass) {
if(value instanceof String){
return Location.findProblem(Long.parseLong((String)value));
}
if(value instanceof Integer){
return Location.findProblem(((Integer)value).longValue());
}
else {
throw context.cannotConvertValueToTargetType(value,targetClass);
}
}
}
and deserialize the json string like this
new JSONDeserializer<Komentar>().use(null, Komentar.class).use(Location.class, new LocationObjectFactory()).deserialize(json);