I found this part of code:
Map<String, Object> myMap = ...
It seems like the above should replace Object with some abstract class or interface that provides more structure for the value in the map. Is there any good reason to directly reference the Object class?
API calls/responses (consuming/producing JSON) will always have a String key but the value may be text, numeric, boolean, array, or an object.
In the specific case of my project we use Spring MVC (which uses Jackson under the hood). Our controllers always consume domain objects directly, e.g. an instance of the User class. Processing a Map with more than a couple of keys is a chore and prone to error.
We frequently return Map<String, Object> because responses almost always include metadata that is generated when the request is made. For example, a GET request made to myapp/api/users might return something like:
{
count: 2,
timestamp: '2020-11-06T17:24:12.123Z',
users: [
{id: 1, firstName: 'Alice', lastName: 'Ackbar'},
{id: 2, firstName: 'Boba', lastName: 'Bling'}
]
}
While the users property contains serialized User entities the remaining fields exist solely for the response. There is no point to creating a UsersResponseEntity class.
Related
Want to convert incoming JSON to a Map<String, Object> but with the following requirements:
Unknow properties should not cause exceptions (objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)).
Only whitelisted properties of the incoming JSON should appear in the Map (with the above configuration even unknown properties would be present in the Map).
The whitelisted properties should automatically correspond to the fields of a POJO.
Converting the JSON directly to my POJO would take care of points 1, 2, and 3, but I would lose the knowledge of which properties were included in the incoming JSON, that's why I want to convert to a Map.
Going from JSON to POJO to Map (let's call it Map2) would also work, but then any primitive fields in the POJO would have default values in Map2, even though they were absent in the JSON.
Put another way, I want the fields in the resultant Map to be the intersection of the properties of the JSON and the fields in the POJO.
For example, given the JSON:
{
"name": "ABCD",
"_email": "abcd#example.com",
"roles": [
"USER"
]
}
where name and roles are valid fields in the POJO (email is valid, but not _email), I want to end up with the Map:
{
name: "ABCD",
roles: [ "USER"]
}
Don't want to have to hand-code the list of valid field names for the various POJOs.
I want to build a data aggregator which calls multiple services and extract some attributes from their response and then build the aggregated object.
Please consider the below example:
To fetch order details, I need to make a call to GetOrderDetails API of OrderService, which returns below output (simplified one):
{
statusCode: OK,
orderDetails: {
orderId: "order_id",
offer: {
offerId: "offer_id",
offerType: "offer_type"
},
address: {
addressId: "address_id",
houseNo: "house_no",
city: "city",
state: "state"
}
}
}
Similarly, I need to make a call other services.
Problem
I need to build output object by cherry picking the attributes from the response of these services without hard coding the logic through getters of the concerned attributes.
I am planning to have a list of needed attributes and their respective paths stored as config and then extracting logic would be generic.
Well, config will look like:
output_attribute: dotted_hierarchy_path
orderId: orderDetails.orderId
addressId: orderDetails.address.addressId
So, my output object will be:
{
orderId: <order_id>,
addressId: <address_id>
}
If I have this kind of configuration, my java code would be generic i.e. it can cherry pick any attribute from any object.
To extract the required attributes, I am thinking is to traverse the dotted path and through Java Reflection get the value. But Java Reflection is slow.
This is a practical world problem and hence want folks to put their valuable approaches.
As you said, reflection is slow and tricky. I wouldn't try to reimplement it from scratch but rather use existing libraries, such as FasterXML/jackson. And I wouldn't worry about performance for now.
In general, I would try to convert objects to nested maps. If you have access to plain jsons, you don't need to map them to objects, this way you skip reflection:
Map<String, Object> root = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
If you have to deal with objects, you can convert them to nested maps in a similar way:
Map<String, Object> root = objectMapper.convertValue(object, new TypeReference<Map<String, Object>>() {});
Getting values by dotted_hierarchy_path from the nested maps should be pretty easy.
What is the basic purpose of #SerializedName annotation in Android using Gson?
Give me some different examples. I can't understand the main purpose of using it.
Java class example,
public class Person {
#SerializedName("name")
private String personName;
#SerializedName("bd")
private String birthDate;
}
This class has two fields that represent the person name and birth date of a person. These fields are annotated with the #SerializedName annotation. The parameter (value) of this annotation is the name to be used when serialising and deserialising objects. For example, the Java field personName is represented as name in JSON.
JSON Example,
{
"name":"chintan",
"bd":"01-01-1990"
}
There are already few answers here,but I would like to add that if you are using ProGuard to Obfuscate your code & don't use #SerializedName("name") in your model class, then your GSON won't work. Because due to obfuscation, your variable names might have changed from String name to String a resulting into broken GSON parsing as GSON will look for key a into json & it will fail.
By specifying #SerializedName, GSON will not look in json based on variable name & will just use specified #SerializedName.
Of Course you can tell proguard to not obfuscate your model, but if you would like to have model obfuscated, then you must specify #SerializedName
Using #SerializedName you are actually telling the Parser when receiving a callback from the server i.e. of a Json format:
{
"name":"John Doe",
}
that when Serializing or Deserializing an object to instead of searching for a key named: "userName", in the Json response, to search for "name".
#SerializedName("name")
var userName: String,
This is good because you may have a model that you would like it to have its members being called with whatever you like.
You can instruct Proguard to not obfuscate your data classes by specifying #Keep on top of the class. This will neither remove nor obfuscate your class. No need to add #SerializedName to each and every field explicitly if the field name is similar to the Json key being used for it.
Let's say in a real-world scenario, your backend dev is giving you this response for an API request you make
{
"name":"John Doe",
"id":"1478"
}
Now, in the data class you make to handle this, there might be chances you want to specify a different variable name at Android side for the fields "name" and "id" that you are getting from backend.
#SerializedName comes to rescue here.
You just need to specify the actual key value you will be getting from backend in the #SerializedName (which will be used to serialize and deserialize) and then you can use a variable name of your choice that stores that value received from the operation.
For example, for the JSON I mentioned earlier, here is how its data class will look like:
data class User(
#SerializedName("name") val userName: String,
#SerializedName("id") val userId: Int
)
Here name, id is used in #SerializedName because it's the backend key.
But I have used userName, userId to store those values.
I've got an object design question.
I'm building a json api in Java. My system uses pojos to represent json objects and translates them from json to pojo using Jackson. Each object needs to take different forms in different contexts, and I can't decide whether to create a bunch of separate classes, one for each context, or try to make a common class work in all circumstances.
Let me give a concrete example.
The system has users. The api has a service to add, modify and delete uses. There is a table of users in a database. The database record looks like this:
{
id: 123, // autoincrement
name: "Bob",
passwordHash: "random string",
unmodifiable: "some string"
}
When you POST/add a user, your pojo should not include an id, because that's autogenerated. You also want to be able to include a password, which gets hashed and stored in the db.
When you PUT/update a user, your pojo shouldn't include the unmodifiable field, but it must include the id, so you know what user you're modifying.
When you GET/retrieve the user, you should get all fields except the passwordHash.
So the pojo that represents the user has different properties depending on whether you're adding, updating, or retrieving the user. And it has different properties in the database.
So, should I create four different pojos in my system and translate among them? Or create one User class and try to make it look different in different circumstances, using Jackson views or some other mechanism?
I'm finding the latter approach really hard to manage.
In my opinion you should create only one POJO - User which has all needed properties. And now you should decide whether your API is rigorous or lenient. If your API is rigorous it should return error when it receives wrong JSON data. In lenient version API can skip superfluous (unnecessary) properties.
Before I will provide an example, let me change the 'passwordHash' property to 'password'.
Add new user/POST
JSON data from client:
{
id: 123,
name: "Bob",
password: "random string",
unmodifiable: "some string"
}
Rigorous version can return for example something like this:
{
"status": "ERROR",
"errors": [
{
"errorType": 1001,
"message": "Id field is not allowed in POST request."
}
]
}
Lenient version can return for example something like this:
{
"status": "SUCCESS",
"warnings": [
"Id field was omitted."
]
}
For each CRUD method you can write a set of unit tests which will be holding information which way you choose and what is allowed and what is not.
I am trying to parse a JSON structure similar to this one:
{
"cars": {
"112": {
"make": "Cadillac",
"model": "Eldorado",
"year": "1998"
},
"642": {
"make": "Cadillac",
"model": "Eldorado",
"year": "1990"
},
"9242": {
"make": "Cadillac",
"model": "Eldorado",
"year": "2001"
}
}}
I have a CarEntity class defined with makeName,model,year attributes defined and accessible via setters/getters.
I am trying to deserialize this JSON like this:
Map<String, CarEntity> deserialized = new JSONDeserializer<Map<String, CarEntity>>()
.use("cars.values", Map.class)
.deserialize(json);
and it doesn't work :( It does deserialize it but not into Map<String, CarEntity> but rather into deep Map(something like Map<String, Map<String, Map<String, String>>> )
What am I doing wrong?
You're problem is your json has two maps. One which contains the 'cars' key, and one that contains the actual CarEntity. Unfortunately, you can't refer to a single key within a Map and assign types on just that key at this time. Generally setting types on values for collections refers to all values within the collection. You don't need to specify the types for the first Map that contains the "cars" key since it will deserialize it by default.
Map<String, CarEntity> deserialized = new JSONDeserializer<Map<String,Map<String, CarEntity>>>()
.use("values.values", CarEntity.class )
.deserialize(json).get("cars");
The path 'values.values' refers to the outer Map's values then traversing the next map values are all CarEntity instances.
I've considered changing the path expressions to be more expressive allowing you to target a single value in a collection, but this increases overhead of evaluating them and being backwards compatible is a challenge.
You are most likely being bitten by Java Type Erasure: JSON library in question does not know type you want; all it sees is equivalent of Map. So you must specify at least value type somehow. Hopefully FlexJSON documentation points out how.
Alternatively you may be able to sub-class HashMap into your own type (MyEntityMap extends HashMap), since then type information can be inferred from generic super type; and passing MyEntityMap.class would give type information that most JSON libraries can use (Jackson and GSON at least).
If these do not work, Jackson and GSON libraries can handle this use case easily; both have methods to specify generic types for deserialization.
Just add one more call to get("cars") like:
Map<String, CarEntity> deserialized = new JSONDeserializer<Map<String, CarEntity>>()
.use("cars.values", Map.class)
.deserialize(json).get("cars");
jSon string was probably serialized from a variable cars typed as Map<String, CarEntity>