Retrofit/Android - dynamic response support - java

I'm having trouble with mapping resulting json data to pojo class with Retrofit. I need to determine Firebase topics by token. This can be eaisly done with Google's json api, as described here: https://developers.google.com/instance-id/reference/server#get_information_about_app_instances
In my case, server response is simlar to this:
{
"applicationVersion": "36",
"connectDate": "2018-02-04",
"attestStatus": "ROOTED",
"application": "<my application id>",
"scope": "*",
"authorizedEntity": "205414012839",
"rel": {
"topics": {
"topic1": {
"addDate": "2018-02-04"
},
"topic2": {
"addDate": "2018-01-31"
}
}
},
"connectionType": "WIFI",
"appSigner": "<hash>",
"platform": "ANDROID"
}
The problem is basically rel and topics structure, because topics is dynamic and field list can by anything and it's unknown. So I can't generate simple POJO to get it mapped by Retrfofit automatically.
Can I force Retrofit to treat topics as single String field, I will able to parse it later to retrieve topics list? Or is there any other soulution?
Any ideas?

If you use gson, you can define rel as a JsonElement. If you use moshi, you can define it as a Map.
for gson:
public class Response{
private String applicationVersion;
private String connectDate;
private String attestStatus;
private String application;
private String scope;
private String authorizedEntity;
private String connectionType;
private String appSigner;
private String platform;
private JsonElement rel;
}
for moshi:
public class Response{
private String applicationVersion;
private String connectDate;
private String attestStatus;
private String application;
private String scope;
private String authorizedEntity;
private String connectionType;
private String appSigner;
private String platform;
private Map<String, Map<String, Map<String, String>>> rel;
}

Related

reading the value of an array of json and putting it into set through java

I am using Jackson library with java 11 so basically I am able to read the below JSON into a string format
{
"schemas":[
"urn:params:core:2.0:User",
"urn:params:core:3.0:User"
],
},
}
here below is the set in which I have to fill the values of schemas from above json
private Set<String> setschemas = null;
right now I am able to read the above json into a string named finaljson , now please advise how can I read the differnt value of schemas from above json string named finaljson and set it to set named setschemas
if (node.has("schemas")) {
// *** here I want to read the differernt value of schemas and set it to a set
// named setschemas
// *****
}
you can create the following classes that represent the json structure
class MyJsonObject {
private AppIdentity appIdentity;
private Set<String> schemas;
private String userName;
}
class AppIdentity {
private String clientId;
private String username;
}
than you can use
final MyJsonObject myJsonObject = new ObjectMapper().readValue(finaljson, MyJsonObject.class); to read the json to JAVA object
so it can manipulated like myJsonObject.schemas.size() > 0 and such...
there are a lot of examples in the internet
*keep in mind, this solution only works when the json structure and fields name are known in advanced
With your approach, this would be simplest one:
if(node.has("schemas")) {
JsonNode schemaNode = node.get("schemas");
Set<String> schemaSet = objectMapper.convertValue(schemaNode, Set.class);
System.out.println("schemaSet" + schemaSet);
}
There are various ways to deal with JSON one is described here
1) You can create a class of JSON structure as follows with help online JSON to POJO convertor (Note:: Add Setters and Getters with help of IDE)
class AppJson {
private Set<AppIdentity> appIdentity;
private Set<String> schemas;
private String userName;
private Manager ManagerObject;
private String division;
private String organization;
private String costCenter;
private String employeeNumber;
}
class AppIdentity {
private String clientId;
private String username;
}
class Manager {
private String value;
private String $ref;
private String displayName;
private String $Ref;
}
2) Use above for object conversion.
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = "{\"appIdentity\":[{\"clientId\":\"9a41763c642\",\"username\":\"XXX\"}],\"schemas\":[\"urn:params:core:2.0:User\",\"urn:params:core:3.0:User\"],\"userName\":\"ajklmnop_699100\",\"manager\":{\"value\":\"string\",\"$ref\":\"sdkoirk\",\"displayName\":\"string\",\"$Ref\":\"sdkweoirk\"},\"division\":\"string\",\"organization\":\"string\",\"costCenter\":\"string\",\"employeeNumber\":\"string\"}\n"
+ "";
AppJson appJson = objectMapper.readValue(jsonString, AppJson.class);
System.out.println("json " + appJson.getSchemas());
Here you will get the schemas.

Java object not populated from json request for inner class

Have searched in different sites but couldn't find correct answer, hence posting this request though it could possible duplicates.sorry for that.
I am sending the below json request to my back-end service and converting to java object for processing. I can see the request body passed to my service but when i convert from json to java object , values are not populating
{
"data":{
"username":"martin",
"customerId":1234567890,
"firstName":"john",
"lastName":"smith",
"password":"p#ssrr0rd##12",
"email":"john.smith#gmail.com",
"contactNumber":"0342323443",
"department":"sports",
"location":"texas",
"status":"unlocked",
"OrderConfigs":[
{
"vpnId":"N4234554R",
"serviceId":"connectNow",
"serviceType":"WRLIP",
"ipAddress":"10.101.10.3",
"fRoute":[
"10.255.253.0/30",
" 10.255.254.0/30"
],
"timeout":1800,
"mapId":"test_map"
}
]
}
}
My Parser class have something like,
JSONObject requestJSON = new JSONObject(requestBody).getJSONObject("data");
ObjectMapper mapper = new ObjectMapper();
final String jsonData = requestJSON.toString();
OrderDTO mappedObject= mapper.readValue(jsonData , OrderDTO .class);
// I can see value coming from front-end but not populating in the mappedObject
My OrderDTO.java
#JsonInclude(value = Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true,value = {"hibernateLazyInitializer", "handler", "created"})
public class OrderDTO {
private String username;
private long customerId;
private String source;
private String firstName;
private String lastName;
private String email;
private String contactNumber;
private String password;
private String department;
private String location;
private String status;
private List<OrderConfig> OrderConfigs;
#JsonInclude(value = Include.NON_NULL)
public class OrderConfig {
private String vpnId;
private String serviceId;
private String serviceType;
private String ipAddress;
private String mapId;
private String[] fRoutes;
private Map<String, Object> attributes;
private SubConfig subConfig;
private String routeFlag;
getter/setters
.....
}
all setter/getter
}
Not sure what I'm missing here. Is this right way to do?
If your are trying to use inner class, correct way to use is to declare it static for Jackson to work with inner classes.
For reference check this
code changes made are
#JsonInclude(value = Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
static class OrderConfig {
Make sure that your json tag names match with variable names of java object
Ex : "fRoute":[
"10.255.253.0/30",
" 10.255.254.0/30"
],
private String[] fRoutes;
OrderConfigs fields will not be initialized, just modify your bean as
#JsonProperty("OrderConfigs")
private List<OrderConfig> orderConfigs;
// setter and getter as setOrderConfigs / getOrderConfigs
See my answer here. (same issue)

Jackson Collection<Object> attributes to top level JSON output string

I'm using Jackson in order to have log outputs as JSON.
The things is that we allow logging with the following syntax:
log.info("yourMessage {}", innerMessageObject, Meta.of("key", ObjectValue))
OUTPUT I HAVE
{
"level": INFO,
... classic logging attributes
"metadata": {
"object1": "value 1",
"object2": { ... }
...
}
}
OUTPUT I WANT
{
"level": INFO,
... classic logging attributes
"object1": "value 1",
"object2": { ... }
...
}
My log POJO
#Data
class JsonLog {
#JsonIgnore
private static final ObjectMapper mapper = JsonLog.initMapper();
private final String message;
private final String class_name;
private final Collection<Object> metadata;
private final Marker marker;
private final String level;
private final Long timestamp;
private final String thread;
private final String logger;
private final LoggerContextVO logger_context;
private final Map<String, String> environment_vars;
}
I don't succeed to only have the metadata attribute to be serialized as top level attributes.
It seems I cannot use #JsonUnwrapped because of this issue, I also tried this solution but cannot see how to implement it.
Have you got any ideas ?
Thank you :)
Would it be OK to convert metadata to be Map<String, Object>? It seems so by you example JSON and that's actually the natural generic representation of JSON in Java. In that case:
private final Map<String, Object> metadata;
#JsonAnyGetter
Map<String, Object> getMetadata() {
return metadata;
}
According to the docs, this annotation marks the getter method:
to be serialized as part of containing POJO (similar to unwrapping)
and can only be used with methods returning a Map. I am not aware of a solution for Collection

Json not converting to object in spring integration

I'm having
.transform(Transformers.fromJson(SNSMessage.class))
not return a value transformed from my JSON. The JSON input is
{
"Type": "Notification",
"MessageId": "b5c64f3f-59e3-5fce-9ad6-1e98973c9537",
"TopicArn": "arn:aws:sns:us-east-1:194477963434:local-hera-update",
"Subject": "com.accuity.hera.model.HeraNotification",
"Message": "{\"id\":\"65e60559-cab5-4027-88a2-46185fbd50b9\",\"resourceType\":\"listItem\",\"action\":\"I\",\"timeOfAction\":\"2017-05-30T19:48:46Z\",\"source\":\"gwl\"}",
"Timestamp": "2017-05-30T19:48:47.593Z",
"SignatureVersion": "1",
"Signature": "Xz0qg0byLMA1fwIRbi7aWcEzhtcLBOmzyUluL1W5URu4WaiEO3G\/+hPSpsFXGxcSYNYRgpKhL9QAP2qLkuMlSEMqiEOHaSr88UaB8QRV2lUEjdBAWpuFYVBPdb+jpo6n3m89vVHoYfFWk8yBkc0zuoRl4OYcUXfTZiWWQkkrT8r9OzWU8LxQwgf0jgr1xEoqbl7uMHIp7nHp3cKstQ0mbK6yxMQ8faxfDm+IwH3k8BBH2\/CXmRg9WME6JK77jvagMUHNhUahWKIjm4iz+TCQCdnmHQR21hmgxlkhdrSxZ1FBbk6BjxfX7gorEwwfY1gYNoZCXxsN63+4vSiFMlOAAQ==",
"SigningCertURL": "https:\/\/sns.us-east-1.amazonaws.com\/SimpleNotificationService-b95095beb82e8f6a046b3aafc7f4149a.pem",
"UnsubscribeURL": "https:\/\/sns.us-east-1.amazonaws.com\/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:194477963434:local-hera-update:7de3da56-9e6c-43ca-9abc-d46fff379380"
}
and the class definition is:
public class SNSMessage {
private String Type;
private String MessageId;
private String TopicArn;
private String Subject;
private String Message;
private String Timestamp;
private String SignatureVersion;
private String Signature;
private String SigningCertURL;
private String UnsubscribeURL;
}
any ideas why the SNSMessage is coming back with all its fields set to null?
The problem you have is that your json attributes are using a different capitalization than your pojo properties.
It means you have Type as json attribute and type detected for your getter.
You need to use #JsonProperty annotation like this:
#JsonProperty("Type")
private String type;
...
// getters / setters for type
Btw, if you don't want to follow the java naming standard and have Type as well as the pojo property, then just add the #JsonProperty to the property with no args

Is there way to associate arbitrary data structure with GSON parser?

First of all I've seen this question, but I did not see the full answer to my question and this question was asked 2 years ago.
Introduction:
For example we have an JSON with such structure:
{
"name": "some_name",
"description": "some_description",
"price": 123,
"location": {
"latitude": 456987,
"longitude": 963258
}
}
I can use GSON library for auto parsing this JSON to my object's class.
For this I must create class describing JSON structure, like below:
public class CustomClassDescribingJSON {
private String name;
private String description;
private double price;
private Location location;
// Some getters and setters and other methods, fields, etc
public class Location {
private long latitude;
private long longitude;
}
}
And next I can auto parse JSON to object:
String json; // This object was obtained earlier.
CustomClassDescribingJSON object = new Gson().fromJson(json, CustomClassDescribingJSON.class);
I have a few ways for changing names of fields in my class (for writing more readable code or to follow language guidelines). One of them below:
public class CustomClassDescribingJSON {
#SerializedName("name")
private String mName;
#SerializedName("description")
private String mDescription;
#SerializedName("price")
private double mPrice;
#SerializedName("location")
private Location mLocation;
// Some getters and setters and other methods, fields, etc
public class Location {
#SerializedName("latitude")
private long mLatitude;
#SerializedName("longitude")
private long mLongitude;
}
}
Using same code like above for parsing JSON:
String json; // This object was obtained earlier.
CustomClassDescribingJSON object = new Gson().fromJson(json, CustomClassDescribingJSON.class);
But I could not find a possibility to change the structure of the class. For example, I would like to use next class for parsing the same JSON:
public class CustomClassDescribingJSON {
private String mName;
private String mDescription;
private double mPrice;
private long mLatitude;
private long mLongitude;
}
Questions:
Same as in the header: Is there way to associate arbitrary data structure with GSON parser?
Maybe there are another libraries to do what I want?
Would a custom GSON (de-)serializer help?
See https://sites.google.com/site/gson/gson-user-guide#TOC-Custom-Serialization-and-Deserialization
Simply convert the JSON string into HashMap<String, Object> then populate any type of custom structure by simply iterating it or create a constructor in each custom object class as shown below to populate the fields.
class CustomClassDescribingJSON {
public CustomClassDescribingJSON(Map<String, Object> data) {
// initialize the instance member
}
}
Sample code:
Reader reader = new BufferedReader(new FileReader(new File("resources/json12.txt")));
Type type = new TypeToken<HashMap<String, Object>>() {}.getType();
HashMap<String, Object> data = new Gson().fromJson(reader, type);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(data));
output:
{
"price": 123.0,
"location": {
"latitude": 456987.0,
"longitude": 963258.0
},
"description": "some_description",
"name": "some_name"
}

Categories