I have the following Json file which I am trying to read:
{
"billingInformation": {
"taxes": {
"gst": 2.5,
"hst": 7.8
},
"billTo": {
"name" : "Mike",
"address" : "123, Lake Shore Drive, California",
"phoneNumber" : "601 855 1249"
},
"salesAgent": {
"name" : "Charlotte Thompson",
"agentCode" : 44551
},
"items": {
"item": [
{
"hsnCode": "5112",
"description": "TV Set",
"originCountry": "US",
"quantity": 1,
"unitPrice": 150.00
}
],
"currency": "USD"
}
}
}
I used direct Object Mapping provided by Gson:
result = new String(Files.readAllBytes(Paths.get(path)));
BillingInformation billingInformation = gson.fromJson(result, BillingInformation.class);
But it always resulted in:
class BillingInformation {
taxes: null
billTo: null
salesAgent: null
items: null
}
How can I get the data inside the other objects?
Edit:
Here is the BillingInformation Class:
public class BillingInformation {
#SerializedName("taxes")
private Taxes taxes;
#SerializedName("billTo")
private BillTo billTo;
#SerializedName("salesAgent")
private SalesAgent salesAgent;
#SerializedName("items")
private Items items;
}
and I have the usual getters and setters for the above fields.
You should create a class that looks like this:
class Data {
private BillingInformation billingInformation;
}
And then do the deserialization:
Data fromFile = gson.fromJson(result, Data.class);
{
"DistributionOrderId" : "Dist_id_1",
"oLPN":
{
"Allocation":
{
"AID": "12345"
},
"Allocation":
{
"AID": "123456" ,
"SerialNbr": "SRL001",
"BatchNbr": "LOT001"
"RevisionNbr": "RVNBR1"
}
},
"oLPN":
{
"Allocation":
{
"AID": "12123"
}
"Allocation":
{
"AID": "12124"
}
}
}
I have a JSON request passed from the vendor, How to store the values as Java POJO and use them further?
Edit : Added attributes to JSON
As you aren't using the usual camel case conventions common in jackson databinding I would suggest defining a java object with the appropriate annotations. The object is not currently valid json. If for example the json looks like this:
{
"DistributionOrderId" : "Dist_id_1",
"oLPN": [ ... ]
}
Where each oLPN in the array is an object like this:
{
"Allocation": [{ "AID": ... }, { "AID": ... }, ... ]
}
You can write a class for distribution orders
public class DistributionOrder {
#JsonProperty("DistributionOrderId") private String id;
#JsonProperty("oLPN") private List<OLPN> olpn;
// getters and setters
}
Then another for the OLPN object
public class OLPN {
#JsonProperty("Allocation") private String allocation;
#JsonProperty("AID") private String aid;
// getters and setters
}
Then you can use an object mapper as appropriate. For example
ObjectMapper mapper = ... // get your object mapper from somewhere
DistributionOrder distributionOrder = mapper.readValue(raw, DistributionOrder.class);
See also object mapper javadoc
json key can't repeat, and the repeat key with value will be discard.
i have format your json text, may be it is like following expression:
{
"DistributionOrderId" : "Dist_id_1",
"oLPN":
[
{
"Allocation":
[
{
"AID": "123456" ,
"SerialNbr": "SRL001",
"BatchNbr": "LOT001",
"RevisionNbr": "RVNBR1"
},
{
"AID": "12345"
}
]
},
{
"Allocation":
[
{
"AID": "12123"
},
{
"AID": "12124"
}
]
}
]
}
and this struct match the java object is
class DistributionOrder{
#JsonProperty("DistributionOrderId")
String distributionOrderId;
List<OLPN> oLPN;
}
class OLPN {
#JsonProperty("Allocation")
List<Allocation> allocation;
}
class Allocation{
String AID;
#JsonProperty("SerialNbr")
String serialNbr;
#JsonProperty("BatchNbr")
String batchNbr;
#JsonProperty("RevisionNbr")
String revisionNbr;
}
I am new with Spring Data MongoDB and I am trying to implement an aggregation query in Java with Spring Data MongoDB. I have tried searching from this problem and approached it using MongoTemplate, but still to no result.
The format of my data:
[{
"_id" : ObjectId("5e1aea6c275360baf96bac29"),
"title" : "postim",
"upvotesBy" : [
"5e18b4c12753608718dfa007",
"5e19ac0f5161a4994ded1f35"
],
"file" : "test",
"description" : "description",
"postedBy" : "5e18b4c12753608718dfa007",
"createdAt" : ISODate("2020-01-12T09:44:12.119+0000"),
"_class" : "com.socialnetwork.post.Post"
},
{
"_id" : ObjectId("5e1aeaf8275360bb4bb47325"),
"title" : "postim2",
"upvotesBy" : [
"5e18b4c12753608718dfa007",
"5e19ac0f5161a4994ded1f35"
],
"file" : "test2",
"description" : "description2",
"postedBy" : "5e18b4c12753608718dfa007",
"createdAt" : ISODate("2020-01-12T09:46:32.909+0000"),
"_class" : "com.socialnetwork.post.Post"
}]
My query:
db.post.aggregate([
{
$match: {}
},
{
$lookup: {
from: "users",
localField: "postedBy",
foreignField: "_id",
as: "user"
}
},
{
$group: {
_id: {
username: "$user.name",
title: "$title",
description: "$description",
upvotes: { $size: "$upvotesBy" },
upvotesBy: "$upvotesBy",
isUpvoted: { $in: [req.query.userId, "$upvotesBy"] },
isPinned: {
$cond: {
if: { $gte: [{ $size: "$upvotesBy" }, 3] },
then: true,
else: false
}
},
file: "$file",
createdAt: {
$dateToString: {
format: "%H:%M %d-%m-%Y",
timezone: "+01",
date: "$createdAt"
}
},
id: "$_id"
}
}
},
{ $sort: { "_id.isPinned": -1, "_id.createdAt": -1 } }
])
This is the query I use in my Javascript backend and I can do this fairly easy with Mongoose. However I am having some difficulty with the Java implementation of it.
private LookupOperation getLookupOperation() {
return LookupOperation.newLookup().from("user")
.localField("postedBy")
.foreignField("_id")
.as("user");
}
#Override
public List<PostSummary> aggregate() {
LookupOperation lookupOperation = getLookupOperation();
return mongoTemplate.aggregate(Aggregation.newAggregation(lookupOperation, Aggregation.group("id")
.addToSet("user.name").as("username")
.addToSet("title").as("title")
.addToSet("description").as("description")
.addToSet("id").as("id")
.push("upvotesBy").as("upvotesBy")
.addToSet("file").as("file")
.addToSet("createdAt").as("createdAt")
), Post.class, PostSummary.class).getMappedResults();
}
When I try to run this I get the following error:
"Cannot convert [] of type class java.util.ArrayList into an instance of class java.lang.Object! Implement a custom Converter<class java.util.ArrayList, class java.lang.Object> and register it with the CustomConversions. Parent object was: com.socialnetwork.post.PostSummary#7159d908"
When I delete the .addToSet("user.name").as("username") from the group aggregation I also get an error from .push("upvotesBy").as("upvotesBy") as it can not convert [] of type class java.util.ArrayList into an instance of class java.lang.String
Also the implementation of the Post Class and the PostSummary Class is simple:
Post.java:
#Document
public class Post {
#Id
private String id;
private String title;
private List<String> upvotesBy;
private String file;
private String description;
private String postedBy;
private Date createdAt = new Date();
// ... Getters and Setters for each field
}
PostSummary.java:
public class PostSummary {
private String username;
private String title;
private String description;
private List<String> upvotesBy;
private String file;
private String createdAt;
private String id;
//... Getters and Setters for the class
}
I also need to implement the isUpvoted and isPinned part of the query, but getting the idea on how to approach the first problem would be a great start.
EDIT: My desired output:
[
{
"username" : "user1",
"title" : "postim2",
"upvotesBy" : [
"5e18b4c12753608718dfa007",
"5e19ac0f5161a4994ded1f35"
],
"file": "file1",
id: "5e18b4c12753608718dber01"
... Other fields of the original post
},
{
"username" : "user2",
"title" : "postim2",
"upvotesBy" : [
"5e18b4c12753608718dfa007",
"5e19ac0f5161a4994ded1f35"
],
id: "5e18b4c12753608718dber02",
"file": "file2",
... Other fields of the original post
}
]
So from the lookup operation I need only to get the name of the user.
Let's do it
We need to update your aggregation to make it work.
Errors:
users's _id is ObjectId type, but in your post you have stored as String, so $lookup should be changed to Uncorrelated sub-queries
We replace $group by '$addFields' which fits better
We add as last stage $project operator to exclude all unsed fields.
db.post.aggregate([
{
$match: {}
},
{
$lookup: {
from: "users",
let: {
postedBy: "$postedBy"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
{
"$toString": "$_id"
},
"$$postedBy"
]
}
}
}
],
as: "user"
}
},
{
$unwind: "$user"
},
{
$addFields: {
id: {
$toString: "$_id"
},
username: "$user.name",
upvotes: {
$size: "$upvotesBy"
},
isUpvoted: {
$in: [
"5e18b4c12753608718dfa007",
"$upvotesBy"
]
},
isPinned: {
$cond: [
{
$gte: [
{
$size: "$upvotesBy"
},
3
]
},
true,
false
]
},
createdAt: {
$dateToString: {
format: "%H:%M %d-%m-%Y",
timezone: "+01",
date: "$createdAt"
}
}
}
},
{
$sort: {
"isPinned": -1,
"createdAt": -1
}
},
{
$project: {
_id: 0,
user: 0,
upvotesBy: 0,
_class: 0
}
}
])
Now, we transform this query to Spring-Data syntax.
Java Implementation
package postman;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.sort;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.unwind;
import java.util.Arrays;
import java.util.List;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
#Service
public class PostmanService {
#Autowired
private MongoTemplate mongoTemplate;
public List<PostSummary> find(String userId){
Aggregation aggregation = Aggregation.newAggregation(
match(new Criteria()),
//lookup("users", "postedBy", "_id", "user")
new AggregationOperation() {
#Override
public Document toDocument(AggregationOperationContext context) {
return new Document("$lookup",
new Document("from", "users")
.append("let", new Document("postedBy", "$postedBy"))
.append("pipeline", Arrays.asList(
new Document("$match",
new Document("$expr",
new Document("$eq", Arrays.asList(
new Document("$toString", "$_id"),
"$$postedBy"
))))))
.append("as", "user"));
}
},
unwind("$user"),
new AggregationOperation() {
#Override
public Document toDocument(AggregationOperationContext context) {
return new Document("$addFields",
new Document("id", new Document("$toString", "$_id"))
.append("username", "$user.name")
.append("upvotes", new Document("$size", "$upvotesBy"))
.append("isUpvoted", new Document("$in", Arrays.asList(userId, "$upvotesBy")))
.append("isPinned", new Document("$cond",
Arrays.asList(new Document("$gte",
Arrays.asList(new Document("$size", "$upvotesBy"), 3)), Boolean.TRUE, Boolean.FALSE)))
.append("createdAt", new Document("$dateToString",
new Document("format", "%H:%M %d-%m-%Y")
.append("timezone", "+01")
.append("date", "$createdAt")
)));
}
},
sort(Direction.DESC, "isPinned", "createdAt"),
project().andExclude("user", "_class")
);
System.out.println("Aggregation: " + aggregation.toString());
return mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Post.class), PostSummary.class).getMappedResults();
}
}
Now, we call aggregation pipeline:
List<PostSummary> l = postmanService.find("5e18b4c12753608718dfa007");
for(PostSummary post: l) {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
System.out.println(ow.writeValueAsString(post));
}
2020-01-12 16:15:22.043 INFO 11148 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-01-12 16:15:22.047 INFO 11148 --- [ main] Postman.PostmanApplication : Started PostmanApplication in 4.602 seconds (JVM running for 5.301)
Aggregation: { "aggregate" : "__collection__", "pipeline" : [{ "$match" : {}}, { "$lookup" : { "from" : "users", "let" : { "postedBy" : "$postedBy"}, "pipeline" : [{ "$match" : { "$expr" : { "$eq" : [{ "$toString" : "$_id"}, "$$postedBy"]}}}], "as" : "user"}}, { "$unwind" : "$user"}, { "$addFields" : { "id" : { "$toString" : "$_id"}, "username" : "$user.name", "upvotes" : { "$size" : "$upvotesBy"}, "isUpvoted" : { "$in" : ["5e18b4c12753608718dfa007", "$upvotesBy"]}, "isPinned" : { "$cond" : [{ "$gte" : [{ "$size" : "$upvotesBy"}, 3]}, true, false]}, "createdAt" : { "$dateToString" : { "format" : "%H:%M %d-%m-%Y", "timezone" : "+01", "date" : "$createdAt"}}}}, { "$sort" : { "isPinned" : -1, "createdAt" : -1}}, { "$project" : { "user" : 0, "_class" : 0}}]}
2020-01-12 16:15:22.161 INFO 11148 --- [ main] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:277}] to localhost:27017
{
"username" : "user1",
"title" : "postim2",
"description" : "description2",
"upvotesBy" : [ "5e18b4c12753608718dfa007", "5e19ac0f5161a4994ded1f35" ],
"file" : "test2",
"createdAt" : "10:46 12-01-2020",
"id" : "5e1aeaf8275360bb4bb47325"
}
{
"username" : "user1",
"title" : "postim",
"description" : "description",
"upvotesBy" : [ "5e18b4c12753608718dfa007", "5e19ac0f5161a4994ded1f35" ],
"file" : "test",
"createdAt" : "10:44 12-01-2020",
"id" : "5e1aea6c275360baf96bac29"
}
i would like to know if rest api while consuming input parameter can do the following:
let's say my json object have the following parameters:
string name;
string adress;
hashmap<string,object> content;
and here's an exemple of what can be sent:
{
"name": "AZ",
"adress": "US",
"content": {
"clients": [
{
"client_ref":"213",
"commands" : {
"subCommands": [
{
"num":"1",
"price":"10euro"
},
{
"num":"12,
"price":"10euro"
}
]
}
},
{
"client_ref":"213",
"commands" : {
"subCommands": [
{
"num":"1",
"price":"10euro"
},
{
"num":"12,
"price":"10euro"
}
]
}
}
]
}
}
the question is can rest build the hashmap where the object can itself have n child of hashmap type ... ?
(i'm using jersey as rest implementation )
Assuming that you have a JSON provider such as Jackson registered and your model class looks like:
public class Foo {
private String name;
private String address;
private Map<String, Object> content;
// Getters and setters
}
The following resource method:
#Path("foo")
public class Test {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response post(Foo foo) {
...
}
}
Can handle a request like:
POST /api/foo HTTP/1.1
Host: example.org
Content-Type: application/json
{
"name": "AZ",
"adress": "US",
"content": {
"clients": [
{
"client_ref": "213",
"commands": {
"subCommands": [...]
}
},
{
"client_ref": "213",
"commands": {
"subCommands": [...]
}
}
]
}
}
content is an Object, not a map.
"content": {
"clients": [
{
"client_ref":"213",
"commands" : {
"subCommands": [
{
"num":"1",
"price":"10euro"
},
{
"num":"12,
"price":"10euro"
}
]
}
},
{
"client_ref":"213",
"commands" : {
"subCommands": [
{
"num":"1",
"price":"10euro"
},
{
"num":"12,
"price":"10euro"
}
]
}
}
]
}
And this is Java Object presentation.
public class Content {
private List<Client> clients;
//Getters and setters
}
public class Client {
private String clientRef;
private List<Command> commands;
//Getters and setters
}
//And so on, define other classes.
To answer your question, yes, you can build a map.
Check this example, please. It tells how to parse an unknown json (in case you don't know the exact structure of your json object).
https://stackoverflow.com/a/44331104/4587961
Then you can build a map with fields
Map<String, Object> where some values of this map will be nested maps.
you can use javax.ws.rs.core.GenericEntity to wrap collections with generic types (your HashMap).
#GET
#Path("/mapping")
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response getAllMapContents() {
Map<String,Object> map = new HashMap<String,Object>();
map.put("Hello", "World");
map.put("employee", new Employee(1,"nomad"));
GenericEntity<Map<String,Object>> entity = new GenericEntity<Map<String,Object>>(map) {};
return Response.ok(entity).build();
}
I checked it and found it working Please find the response below. Thank you.
{
"Hello": "World",
"employee": {
"id": 1,
"name": "nomad"
}
}
Below is the Json I am converting to a POJO using jackson:
[
[
{
"title" : "Professional JavaScript",
"author" : "Nicholas C. Zakas"
},
{
"title" : "JavaScript: The Definitive Guide",
"author" : "David Flanagan
},
],
[
{
"title" : "High Performance JavaScript",
"author" : "Nicholas C. Zakas"
},
{
"title" : "Professional JavaScript",
"author" : "Nicholas C. Zakas"
},
{
"title" : "JavaScript: The Definitive Guide",
"author" : "Nicholas C. Zakas"
}
]
]
I am getting the following error
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.test.sample.jsonsample out of START_ARRAY token
below is my class
public class jsonsample
{
#JsonProperty("title")
public String title;
#JsonProperty("author")
public String author;
//getters and setters
}
public class mainclass
{
public List<jsonsample> sjson;
}
ObjectMapper mapper = new ObjectMapper();
List<mainclass> accs = mapper.readValue(rspString,mapper.getTypeFactory().constructCollectionType(List.class, mainclass.class));
How can I get data from the nested structure, and also with unnamed arrays?