REST - How to minify Json Response - java

I have a JAVA REST service that returns a list of objects. Each object contains name, description, code.
I want to minify response json
{
"objects": {
"count": 10000,
"list": [
{
"name": "1",
"description": "foo",
"code": "foo",
},
{
"name": "2",
"description": "bar",
"code": "bar",
},
...... (1.000 items)
]
}
}
TO:
{
"a": {
"b": 1000,
"c": "a:objects,b:count,c:mapping,d:list,e:name,f:description,g:code",
"d": [
{
"e": "1",
"f": "foo",
"g": "foo",
},
{
"e": "2",
"f": "bar",
"g": "bar",
},
...... (1.000 items)
]
}
}
how can i do it, thanks.

Even though we don't know what specific technologies you are using. I am going to make an assumption that you are using some sort of REST library like Spring or JaxRS and you are serializing POJOs into the JSON. I will also make the assumption that you have everything setup and working for that configuration and I will focus on the output specifically using that setup.
If you are using something like Jackson for your POJO, you can add the following Annotation to your class:
public class MyResponseObject {
#JsonProperty("a")
private MyObject objects;
public MyObject getObjects() { return objects; }
public void setObjects(MyObject object) { this.objects = object; }
}
public class MyObject {
#JsonProperty("b")
private long count;
#JsonProperty("d")
private List<Item> list;
// getters/setters
}
public class Item {
#JsonProperty("e")
private Sting name;
#JsonProperty("f")
private String description;
#JsonProperty("g")
private String code;
// getters/setters
}
In regards to the mapping of what each of those mean, you can hard/code that mapping, but I don't think there is an automatic way to do that. You can also include what that mapping is in the JavaDoc for your method. Another alternative is if this is an API that is public to other services, you can not only provide documentation, but also a packaged Jar with the POJOs that your API puts out. This way all they have to do is include your jar file as a dependency and include them in the mapped classes.
I hope this helps guide you in the right direction.
Also, if you don't use Jackson, but prefer JAXB use #XmlElement(name="a")

Related

Map with object and list of objects to Json

I have got two main model classes: Customer and Product
public class Customer {
String name;
String surname;
int age;
BigDecimal cash;
}
public class Product {
String name;
Category category;
BigDecimal price;
}
I want to build json file with Map<Customer, List<Product>>
When I write to json file data with my method which works correct - I am sure about this - the json file shows this syntax
{
"Customer{name\u003d\u0027Custo1\u0027, surname\u003d\u0027Surname\u0027, age\u003d18, cash\u003d1200}": [
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
}
Then when i want to read this file, the error Exception in thread "main" java.util.NoSuchElementException: No value present occurs so I think that the Customer syntax from json file is not recognized.
So I tried to write data to json file on my own with this syntax below, but it does not work
[
{
"name": "Abc",
"surname": "Def",
"age": 14,
"cash": "2000"
}
:
[
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
]
json converter method:
public void toJson(final T item) {
try (FileWriter fileWriter = new FileWriter(jsonFilename)) {
fileWriter.write(gson.toJson(item));
} catch (Exception e) {
throw new ValidatorException(e.getMessage());
}
}
#Tom is right on the issues you've faced with. I'll explain why and suggest one more solution.
Your first JSON is technically a valid JSON but it cannot be deserialized, because the map keys are results of the Customer.toString() method Gson uses by default. This is why it looks weird, acts like a debug string, and can't be deserialized back: there it is almost always no way to restore an object from the toString() result (toString is designed mostly for debugging/logging purposes providing basic information regarding the state of a particular object that does not need to expose its all internals at all).
Your second JSON is invalid JSON. Period.
Tom's suggestion of making the list of products a part of the customer class is totally fine. Having it implemented like that lets you to serialize everything as a list like this:
[
{
"name": "john",
"products": [
{"name": "prod1"},
{"name": "prod2"}
]
}
]
Hint: separating domain objects (Customer and Product) and representation objects for data transfer (CustomerDto and ProductDto) is usually a fine idea too since it allows to create representation for any concrete representation implementation (one for various JSON implementation libraries, two for other-format-oriented tools, third for persistence, four for UI views, etc), so it might be implemented like converting Map<Customer, List<Product>> to List<CustomerDto> and back (possibly by using mapper-generators like MapStruct).
If for whatever reason it is not possible to reorganize your domain classes or create Gson-friendly DTO-mappings, or you're fine to keep it as simple as possible and you're fine with having not that trivial JSON structure (as long as you understand implications of the format in this solution: evolution, distribution, etc), then you can enable special Gson mode to support this kind of maps. It generates valid JSONs that can be serialized and deserialized back, but the way it is implemented looks a bit of anti-pattern to me because of losing semantics due to using arrays as the data container.
#AllArgsConstructor
#EqualsAndHashCode
#ToString
final class Customer {
final String name;
}
#AllArgsConstructor
#EqualsAndHashCode
#ToString
final class Product {
final String name;
}
public final class MapTest {
private static final Gson gson = new GsonBuilder()
.enableComplexMapKeySerialization()
.create();
private static final TypeToken<Map<Customer, List<Product>>> customerToProducts = new TypeToken<Map<Customer, List<Product>>>() {};
#Test
public void test() {
final Map<Customer, List<Product>> ordersBefore = ImmutableMap.of(
new Customer("john"), ImmutableList.of(new Product("prod1"), new Product("prod2"))
);
final String json = gson.toJson(ordersBefore, customerToProducts.getType());
Assertions.assertEquals("[[{\"name\":\"john\"},[{\"name\":\"prod1\"},{\"name\":\"prod2\"}]]]", json);
final Map<Customer, List<Product>> ordersAfter = gson.fromJson(json, customerToProducts.getType());
Assertions.assertEquals(ordersBefore, ordersAfter);
}
}
Note that it generates JSON like this (index 0 means the key, index 1 means the value):
[
[
{"name": "john"},
[
{"name": "prod1"},
{"name": "prod2"}
]
]
]

How to manipulate nested Json Data to de-Nest in java

I have been looking solution for this problem but could not find one so asking this question.
I have some data which looks like this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation": {
"firstName": "jio",
"lastName": "g",
"mobileNumber": "1234567890",
},
"accountBalance": 0,
}
]
},
I want to write a java code to change the data structure to this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation_firstName":"jio",
"personalInformation_lastNAme":"g",
"personalInformation_mobileNumber":"1234567890",
"accountBalance": 0,
}
]
},
I am getting data from db as:
#Override
public List<User> getAllUsers() {
logger.debug("entering all users method");
List<User> allUsers=mongoOperations.findAll(User.class);
for (User user : allUsers) {
PersonalInformation info=user.getPersonalInformation());
//manipulation code here
user.setPersonalInformation(info);
}
return allUsers;
}
So I want to write a logic so that i can convert the data in desired format and send it a return type. I know how to do same thing using J query but I want to do it in backend so any code for the above or any link will help.
I have fond one solution which is very simple.So, basically when we create object for nested data we create it like this in JAVA.
public MyClass{
public String name;
public String contact;
public PersonalInformation personalinformation;
//setters and getter here
}
this will give me data as
"MyClass":{
"name": "abc",
"contact": "12345",
"personalInformation":{
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
}
so to remove this nested data we need to use #JsonUnwrapped which removes all the nested object and add it to our main object.
public MyClass{
public String name;
public String contact;
#JsonUnwrapped
public PersonalInformation personalinformation;
//setters and getter here
}
which will change the data structure as:
"MyClass":{
"name": "abc",
"contact": "12345",
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
for more reference you can check this link http://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html
Hope this helps.
There are multiple possible solutions. As Prabhav has mentioned the most intuitive one would be to create a new class and from there a object which can be transformed with a library to a JSON.
Variant one:
The new class would look like your data structure you want and access would be:
PersonalInformationJson pf = new PersonalInformationJson();
pf.setFirstName = info.getPersonalInformation_firstName
//... setting the rest of the object
//using jackson
ObjectMapper mapper = new ObjectMapper();
try {
// convert user object to json string and return it
String jsonString = mapper.writeValueAsString(u);
}
The other easier version to create a string, either per hand or use a lib:
// using org.json.JSONObject
String jsonString = new JSONObject().put("personalInformation_firstName", info.value())
.put("personalInformation_lastNAme", info.value());

Map JSON array to List using RestTemplate

I'm having trouble mapping the environments array in the below JSON response output into a list of Environment objects using RestTemplate.
The name property of ApplicationData is being populated with Deploy app which is correct. However, the list of environments in Application Data is null. I'd like it to be populated with the corresponding id's and names from the output.
JSON output
{
"id": 4587521,
"oid": 8738390651982315522,
"name": "Deploy app",
"description": "",
"environments": [
{
"id": 4751361,
"name": "Stage",
"position": 0,
"configurationState": "TASKED"
},
{
"id": 4751362,
"name": "Dev",
"position": 1,
"configurationState": "TASKED"
},
]
}
ApplicationData.java
#JsonIgnoreProperties(ignoreUnknown = true)
public class ApplicationData {
private String name;
private List<Environment> environments;
// Getters and setters omitted
}
Environment.java
public class Environment {
private String id;
private String name;
// Getters and setters omitted
}
RestTemplate code
List<ApplicationData> applications = new ArrayList<>();
ResponseEntity<List<ApplicationData>> response =
restTemplate.exchange("/deploy/project/all", HttpMethod.GET, null,
new ParameterizedTypeReference<List<ApplicationData>>() {});
applications = response.getBody();
I ended up modifying my ApplicationData class so the property was an array of Environment objects:
private Environment[] environments;
It then populated correctly.
But what I do not get is, you serialize to List<ApplicationData> but your JSON output snippet shows only a single ApplicationData object, not a list with this single object. Did you apply any Jackson Deserialization Features like ACCEPT_SINGLE_VALUE_AS_ARRAY?

How construct list of objects from json string coming from api response?

Similar question might be asked before on here, but I had no luck and I was wondering how to extract specific objects like user in from below json string and then construct an ArrayList. However, there is one twist, one of the property directly under Users is a random number, which can be anything!!!
Here is how my json string looks like:
<code>{
"_links": {
},
"count": {
},
"users": {
"123321": { //*Is a random number which can be any number
"_links": {
},
"user": {
"id": "123321",
"name": "...",
"age": "...",
"address": ""
..
}
},
"456654": {
"_links": {
},
"user": {
"id": "456654",
"name": "...",
"age": "...",
"address": ""
...
}
}
...
},
"page": {
}
}
</code>
The java object I would like to transform it to is:
#JsonIgnoreProperties(ignoreUnknown = true) // Ignore any properties not bound here
public class User {
private String id;
private String name;
//setter:getter
}
Note: The transformation should only consider those two fields (id,name), and ignore the rest of the fields from the json response user:{} object.
Ideally, I would like to end up with a list like this:
List<User> users = resulted json transformation should return a list of users!!
Any idea how can I do this please ideally with Jackson JSON Parser/ or maybe GSON?
Since the user keys are random, you obviously can't map them to a named Java field. Instead, you can parse the top-level object as a map and the manually pull out the user objects.
public class UserWrapper {
private User user;
public User getUser() { return user; }
}
public class Root {
private Map<String, UserWrapper> users;
public List<User> getUsers() {
List<User> usersList = new ArrayList();
for (String key : map.keySet()) {
UserWrapper wrapper = map.get(key);
usersList.add(wrapper.getUser());
}
return userList;
}
}
Root root = parseJson();
List<User> users = root.getUsers()
Hope that helps!
jolt transformer is your friend. Use shift with wildcard * to capture arbitrary node value and then standard mappers (Jackson /gson) .

How To Use Jackson's #JsonIdentityInfo for Deserialization of directed Graphs?

I want to use Jackson 2.3.3 for Deserialization/Serialization of directed graphs. The structure I came up with is roughly the following:
public Class Graph {
private final Set<Node> nodes;
public Graph(Set<Node> nodes) { ... }
public Set<Node> getNodes() { ... }
}
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "name")
public Class Node {
private final String name;
private final Set<Edge> edges;
public Node(String name, Set<Edge> edges) { ... }
public String getName() { ... }
public Set<Edge> getEdges() { ... }
}
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "name")
public Class Edge {
private final String name;
private final Node successor;
public Edge(String name, Node successor) { ... }
public String getName() { ... }
public Node getSuccessor() { ... }
}
And I expect to have this JSON-Structure:
{
"graph": [{
"name": "A",
"edges": [{
"name": "0",
"successor": "B"
}, {
"name": "1",
"successor": "A"
}]
}, {
"name": "B",
"edges": [{
"name": "0",
"successor": "A"
}, {
"name": "1",
"successor": "B"
}]
}]
}
But I get the following error while deserialization (even with annotation #JsonProperty("name") at the Getters):
com.fasterxml.jackson.databind.JsonMappingException: Invalid Object Id definition for some.package.graph.Node: can not find property with name 'name'
I have found some solutions for Jackson 1.6 with Back-Reference Annotations, but I'd like to use the new Jackson 2.x Annotation, as it was advertised so much in the API Update from 1.9 to 2.0 of Jackson.
What point am I missing here? Thanks for constructive answers in advance.
EDIT
(Removed my answer from here to the Answer section)
I got kind of blind of staring too long at it. Here's what's gone wrong:
The Serialization actually worked as intended. What didn't work was the Deserialization, because Jackson wasn't able to instantiate my Node-Object. I simply forgot to annotate the parameters of the constructor methods correctly.
I was now facing another problem. The generated JSON now looked like this:
"graph": {
"nodes": [{
"name": "B",
"edges": [{
"label": "1",
"successor": "B"
}, {
"label": "0",
"successor": {
"name": "A",
"edges": [{
"label": "1",
"successor": "A"
}, {
"label": "0",
"successor": "B"
}]
}
}]
}, "A"]
}
So far so good. But during mapping, Jackson confronts me with this Error:
java.lang.IllegalStateException: Could not resolve Object Id [B] (for [simple
type, class some.package.graph.Node]) -- unresolved forward-reference?
I even changed the Label of the edges because I thought the same property name might confuse Jackson here, but that didn't help either...
My guess here is that Jackson can't reference the Node B, because it is still being constructed (you could say it is actually some kind of root in this example). The only way to fix this seems to construct all the Nodes without the edges and inject them in a second step.

Categories

Resources