How can I match search result in Elasticsearch to the Java Model? - java

Firstly, my project has MinIO server where I use minio api to upload my files via spring boot application. The MinIO server integrated with Elasticsearch. When I uploaded a file, MinIO automatically update Elasticsearch minio_events index (I have configured this settings before). I want to run following query in my spring boot application and match result into File.java:
POST http://localhost:9200/minio_events/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"Records.s3.object.userMetadata.X-Amz-Meta-Filename": "myfile.txt"
}
},
{
"match": {
"Records.s3.object.userMetadata.X-Amz-Meta-User_id": "40b3c4e0-fea8-4aca-9dec-b4b905f33df0"
}
}
]
}
},
"fields": [
"Records.s3.object.userMetadata.X-Amz-Meta-Filename",
"Records.s3.object.userMetadata.X-Amz-Meta-Description",
"Records.s3.object.userMetadata.X-Amz-Meta-Foldername",
"Records.s3.object.userMetadata.X-Amz-Meta-Tags",
"Records.s3.object.userMetadata.X-Amz-Meta-User_id"
],
"_source": false
}
The query result is:
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.7260925,
"hits": [
{
"_index": "minio_events",
"_type": "_doc",
"_id": "AUuxte4_W8625RK6e6oT7tCJmJkSQJ0L9LGx6eAf0Dw=",
"_score": 1.7260925,
"fields": {
"Records.s3.object.userMetadata.X-Amz-Meta-Foldername": [
"helloworld"
],
"Records.s3.object.userMetadata.X-Amz-Meta-Description": [
"des"
],
"Records.s3.object.userMetadata.X-Amz-Meta-User_id": [
"40b3c4e0-fea8-4aca-9dec-b4b905f33df0"
],
"Records.s3.object.userMetadata.X-Amz-Meta-Filename": [
"MyFile.txt"
],
"Records.s3.object.userMetadata.X-Amz-Meta-Tags": [
"hello,world"
]
}
}
]
}
}
In my Spring Boot App, I wrote following repository class to fetch Elasticsearch results.
CustomFileRepository.java:
package com.oktaykcr.fileservice.repository;
import com.oktaykcr.fileservice.model.File;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
#Component
public class CustomFileRepository {
private final ElasticsearchOperations elasticsearchOperations;
private final List<String> fields = List.of(
"Records.s3.object.userMetadata.X-Amz-Meta-Filename",
"Records.s3.object.userMetadata.X-Amz-Meta-Description",
"Records.s3.object.userMetadata.X-Amz-Meta-Foldername",
"Records.s3.object.userMetadata.X-Amz-Meta-Tags",
"Records.s3.object.userMetadata.X-Amz-Meta-User_id"
);
public CustomFileRepository(ElasticsearchOperations elasticsearchOperations) {
this.elasticsearchOperations = elasticsearchOperations;
}
public List<File> findByFileNameAndUserId(String fileName, String userId) {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("Records.s3.object.userMetadata.X-Amz-Meta-Filename", fileName))
.must(QueryBuilders.matchQuery("Records.s3.object.userMetadata.X-Amz-Meta-User_id", userId));
Query query = new NativeSearchQuery(queryBuilder);
query.setFields(fields);
SearchHits<File> result = elasticsearchOperations.search(query, File.class);
if(result.isEmpty()) {
return Collections.emptyList();
}
List<File> files = result.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
return files;
}
}
File.java:
package com.oktaykcr.fileservice.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.util.List;
#Document(indexName = "minio_events")
public class File {
#Id
private String id;
#Field(type = FieldType.Object, value = "fields")
private Fields fields;
public File() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Fields getFields() {
return fields;
}
public void setFields(Fields fields) {
this.fields = fields;
}
static class Fields {
#Field(type = FieldType.Nested, value = "Records.s3.object.userMetadata.X-Amz-Meta-Foldername")
public List<String> folderName;
#Field(type = FieldType.Nested, value = "Records.s3.object.userMetadata.X-Amz-Meta-Description")
public List<String> description;
#Field(type = FieldType.Nested, value = "Records.s3.object.userMetadata.X-Amz-Meta-User_id")
public List<String> userId;
#Field(type = FieldType.Nested, value = "Records.s3.object.userMetadata.X-Amz-Meta-Filename")
public List<String> fileName;
#Field(type = FieldType.Nested, value = "Records.s3.object.userMetadata.X-Amz-Meta-Tags")
public List<String> tags;
public Fields() {
}
public List<String> getFolderName() {
return folderName;
}
public void setFolderName(List<String> folderName) {
this.folderName = folderName;
}
public List<String> getDescription() {
return description;
}
public void setDescription(List<String> description) {
this.description = description;
}
public List<String> getUserId() {
return userId;
}
public void setUserId(List<String> userId) {
this.userId = userId;
}
public List<String> getFileName() {
return fileName;
}
public void setFileName(List<String> fileName) {
this.fileName = fileName;
}
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
}
}
However, the result of List<File> files = customFileRepository.findByFileNameAndUserId(fileName, userId); is:
result = {ArrayList#15401} size = 1
0 = {File#15403}
id = "AUuxte4_W8625RK6e6oT7tCJmJkSQJ0L9LGx6eAf0Dw="
fields = null
id was mapped by #Document model but fields was not.

Related

Java mapping JSON with POJO using jackson

I want to create this JSON using jakson annotated POJOS. The issue I have when I create a new class without #JsonProperty annotation to represent the last {"id":"123ccc","role":"dddd"}, it by default take the class name and create something like "customer":{"id": "123ccc","role":"dddd"}.
The JSON Structure I indent to build
{
"relatedParty": [
{
"contact": [
{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
},
{
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}
],
"role": "ccc",
"fullName": "ccc"
},
{
"id": "123ccc",
"role": "dddd"
}
]
}
The JSON I'm receiving from the below code.
{
"relatedParty": [
{
"contact": [
{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
},
{
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}
],
"role": "ccc",
"fullName": "ccc"
},
"customer" : {
"id": "123ccc",
"role": "dddd"
}
]
}
What would be a workaround to get the exact JSON format as the image. Current Implementation is below.
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class RelatedParty {
#JsonProperty(value = "contact")
private List<Contact> contact;
#JsonProperty(value = "role")
private String role;
#JsonProperty(value = "fullName")
private String fullName;
private Customer customer;
public List<Contact> getContact() {
return contact;
}
public void setContact(List<Contact> contact) {
this.contact = contact;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
public class Customer {
#JsonProperty(value = "id")
private String id;
#JsonProperty(value = "role")
private String role;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
You need to create additional and different POJO classes to model your JSON correctly. Basically, JSON arrays will be handle in Java lists, and JSON objects will be handled in Java classes.
Starting from the inside (most nested level) of the JSON, and working our way out:
NOTE: getters and setters not shown here
Characteristic.java
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Characteristic {
#JsonProperty("city")
private String city;
#JsonProperty("country")
private String country;
#JsonProperty("emailAddress")
private String emailAddress;
}
Contact.java (contains our characteristics):
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Contact {
#JsonProperty("mediumType")
private String mediumType;
#JsonProperty("characteristic")
private Characteristic characteristic;
}
The above two classes handle the innermost objects. If we remove them from your target JSON, that leaves the following:
{
"relatedParty": [{
"contact": [...],
"role": "ccc",
"fullName": "ccc"
}, {
"role": "dddd",
"id": "123ccc"
}]
}
Note that the contact field is a JSON array, not an object - so we do not create a Java Contact class (which would be for a JSON object).
To handle the above I create two more classes:
RelatedPartyInner.java (contains a list of contacts)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RelatedParty_ {
#JsonProperty("contact")
private List<Contact> contact = null;
#JsonProperty("role")
private String role;
#JsonProperty("fullName")
private String fullName;
#JsonProperty("id")
private String id;
}
RelatedParty.java (wraps everything in an outer object):
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RelatedParty {
#JsonProperty("relatedParty")
private List<RelatedPartyInner> relatedParty = null;
}
To test this I create the following data:
Characteristic chr1 = new Characteristic();
chr1.setCity("xxx");
chr1.setCountry("xxx");
Characteristic chr2 = new Characteristic();
chr2.setEmailAddress("yyy#yy.yyy");
Contact con1 = new Contact();
con1.setMediumType("xxx");
con1.setCharacteristic(chr1);
Contact con2 = new Contact();
con2.setMediumType("yyy");
con2.setCharacteristic(chr2);
List<Contact> cons = new ArrayList<>();
cons.add(con1);
cons.add(con2);
RelatedPartyInner rpi1 = new RelatedPartyInner();
rpi1.setContact(cons);
rpi1.setRole("ccc");
rpi1.setFullName("ccc");
RelatedPartyInner rpi2 = new RelatedPartyInner();
rpi2.setId("123ccc");
rpi2.setRole("dddd");
List<RelatedPartyInner> rpis = new ArrayList<>();
rpis.add(rpi1);
rpis.add(rpi2);
RelatedParty rp = new RelatedParty();
rp.setRelatedParty(rpis);
Finally, we can generate the JSON:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(new File("rp.json"), rp);
The resulting file contains the following:
{
"relatedParty": [{
"contact": [{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
}, {
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}],
"role": "ccc",
"fullName": "ccc"
}, {
"role": "dddd",
"id": "123ccc"
}]
}

Aggregation results cannot properly map to Java object

I'm trying to get schedules data from mongoDb.
I created the appropriate aggregation and tried to convert it within Spring Framework.
db.theaters.aggregate([
{ $match: { 'city_id': <someCityId>, 'theatreRooms.schedules.spectacle_id': <someSpecId> } },
{ $unwind: '$theatreRooms' },
{ $unwind: '$theatreRooms.schedules' },
{ $group: { _id: { name: '$name', room: '$theatreRooms.name' }, schedules: { $addToSet: '$theatreRooms.schedules.time' } } },
{ $group: { _id: '$_id.name', schedules: { $addToSet: { room: '$_id.room', schedules: '$schedules' } } } }
])
I've created properly match and unwind operations. But I've got problem with first group operation.
It seems that the operation is well interpreted, but for some reason I am not able to properly map the _id object.
Here is my code example:
public class TheaterProject {
private TheaterId _id;
private List<String> schedules;
public TheaterId get_id() {
return _id;
}
public void set_id(TheaterId _id) {
this._id = _id;
}
public List<String> getSchedules() {
return schedules;
}
public void setSchedules(List<String> schedules) {
this.schedules = schedules;
}
}
public class TheaterId {
#Field("name")
private String name;
#Field("room")
private Integer room;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getRoom() {
return room;
}
public void setRoom(Integer room) {
this.room = room;
}
}
public Document getRawSchedules(String cityId, String spectaclesId){
MatchOperation match = Aggregation.match(Criteria.where("city_id").is(cityId).and("theatreRooms.schedules.spectacle_id").is(spectaclesId));
UnwindOperation theaterUnwind = Aggregation.unwind("theatreRooms");
UnwindOperation schedulesUnwind = Aggregation.unwind("theatreRooms.schedules");
GroupOperation firstGroup = Aggregation.group(Fields.from(
Fields.field("name", "name"),
Fields.field("room", "theatreRooms.name")))
.addToSet("theatreRooms.schedules.time").as("schedules");
Aggregation agg = Aggregation.newAggregation(match,theaterUnwind,schedulesUnwind,firstGroup);
Document theaters = mongoTemplate.aggregate(agg, Theater.class, TheaterProject.class).getRawResults();
return theaters;
}
public List<TheaterProject> getSchedules(String cityId, String spectaclesId){
MatchOperation match = Aggregation.match(Criteria.where("city_id").is(cityId).and("theatreRooms.schedules.spectacle_id").is(spectaclesId));
UnwindOperation theaterUnwind = Aggregation.unwind("theatreRooms");
UnwindOperation schedulesUnwind = Aggregation.unwind("theatreRooms.schedules");
GroupOperation firstGroup = Aggregation.group(Fields.from(
Fields.field("name", "name"),
Fields.field("room", "theatreRooms.name")))
.addToSet("theatreRooms.schedules.time").as("schedules");
Aggregation agg = Aggregation.newAggregation(match,theaterUnwind,schedulesUnwind,firstGroup);
List<TheaterProject> theaters = mongoTemplate.aggregate(agg, Theater.class, TheaterProject.class).getMappedResults();
return theaters;
}
When I've invoked method getSchedules which return mapped objects, _id field is equal to null.
[
{
"_id": null,
"schedules": [
"5:15"
]
},
{
"_id": null,
"schedules": [
"6:55",
"4:35",
"10:15"
]
}
]
But when I've invoked getRawSchedules which used getRawResults it's looking properly.
{
"results": [
{
"_id": {
"name": "Pinokio",
"room": 2
},
"schedules": [
"5:15"
]
},
{
"_id": {
"name": "Roma",
"room": 1
},
"schedules": [
"6:55",
"4:35",
"10:15"
]
}
]
}
I don't have any idea why it's working like that.
I didn't find any information about this problem in the documentation and here. But I have a solution. You may just rename the field from _id to something else. theaterId for example. I don't know all requirements for your issue but you may do it just on mapping level.
Fix the mapping
import org.springframework.data.mongodb.core.mapping.Field;
import java.util.List;
public class TheaterProject {
#Field("theaterId")
private TheaterId _id;
private List<String> schedules;
public TheaterId get_id() {
return _id;
}
public void set_id(TheaterId _id) {
this._id = _id;
}
public List<String> getSchedules() {
return schedules;
}
public void setSchedules(List<String> schedules) {
this.schedules = schedules;
}
}
But it requires additional projection step
public List<TheaterProject> getSchedules(String cityId, String spectaclesId){
...
GroupOperation firstGroup = Aggregation.group(Fields.from(
Fields.field("name", "name"),
Fields.field("room", "theatreRooms.name")))
.addToSet("theatreRooms.schedules.time").as("schedules");
ProjectionOperation projection = Aggregation.project(Fields.from(
Fields.field("theaterId", "_id"),
Fields.field("schedules", "schedules")));
Aggregation agg = Aggregation.newAggregation( ... ,firstGroup, projection);
List<TheaterProject> theaters = mongoTemplate.aggregate(agg, "collectionName", TheaterProject.class).getMappedResults();
return theaters;
}

Parsing JSON and Converting in List Of Object

I have a json response received from API Call the sample response is something like this
{
"meta": {
"code": "200"
},
"data": [
{
"Id": 44,
"Name": "Malgudi ABC"
},
{
"Id": 45,
"Name": "Malgudi, DEF"
}
]
}
I am trying to make List of Object from it, the code that i've written for this is
private static List<TPDetails> getListOfTpDetails(ResponseEntity<?> responseEntity){
ObjectMapper objectMapper = new ObjectMapper();
List<TPDetails> tpDetailsList = objectMapper.convertValue(responseEntity.getBody().getClass(), new TypeReference<TPDetails>(){});
return tpDetailsList;
}
Where TPDetails Object is Like this
public class TPDetails {
int Id;
String Name;
}
the code which i have used is resulting in
java.lang.IllegalArgumentException: Unrecognized field "meta" (class com.sbo.abc.model.TPDetails), not marked as ignorable (2 known properties: "Id", "Name"])
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.sbo.abc.model.TPDetails["meta"])
I want to convert the Above JSON response in List
List<TPDetails> abc = [
{"Id": 44, "Name": "Malgudi ABC"},
{"Id": 45,"Name": "Malgudi DEF"}
]
Any help would be highly appreciable.Thanks well in advance
Create 2 more classes like
public class Temp {
Meta meta;
List<TPDetails> data;
}
public class Meta {
String code;
}
and now convert this json to Temp class.
Temp temp = objectMapper.convertValue(responseEntity.getBody().getClass(), new TypeReference<Temp>(){});
UPDATED :
Make sure responseEntity.getBody() return the exact Json String which you mentioned above.
Temp temp = objectMapper.readValue(responseEntity.getBody(), new TypeReference<Temp>(){});
The format of your java class does not reflect the json you are parsing. I think it should be:
class Response {
Meta meta;
List<TPDetails> data;
}
class Meta {
String code;
}
You should then pass Response to your TypeReference: new TypeReference<Response>(){}
If you don't care about the meta field, you can add #JsonIgnoreProperties
to your response class and get rid of the Meta class and field.
Create/update following class, I am storing JSON file, since do not have service, but should be fine and Able to parse it and read it from the following model.
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.List;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"meta",
"data"
})
public class OuterPoJo {
#JsonProperty("meta")
private Meta meta;
#JsonProperty("data")
private List<TPDetails> data = null;
#JsonProperty("meta")
public Meta getMeta() {
return meta;
}
#JsonProperty("meta")
public void setMeta(Meta meta) {
this.meta = meta;
}
#JsonProperty("data")
public List<TPDetails> getData() {
return data;
}
#JsonProperty("data")
public void setData(List<TPDetails> data) {
this.data = data;
}
}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"code"
})
public class Meta {
#JsonProperty("code")
private String code;
#JsonProperty("code")
public String getCode() {
return code;
}
#JsonProperty("code")
public void setCode(String code) {
this.code = code;
}
}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"Id",
"Name"
})
public class TPDetails {
#JsonProperty("Id")
private Integer id;
#JsonProperty("Name")
private String name;
#JsonProperty("Id")
public Integer getId() {
return id;
}
#JsonProperty("Id")
public void setId(Integer id) {
this.id = id;
}
#JsonProperty("Name")
public String getName() {
return name;
}
#JsonProperty("Name")
public void setName(String name) {
this.name = name;
}
}
import java.io.File;
public class App {
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
OuterPoJo myPoJo = objectMapper.readValue(
new File("file.json"),
OuterPoJo.class);
for (TPDetails item : myPoJo.getData()) {
System.out.println(item.getId() + ":" + item.getName());
}
}
}
output:
44:Malgudi ABC
45:Malgudi, DEF

Convert MongoDB entry to Java Class Model Object

I am storing a custom java object called Device into a mongo collection. This works fine. But when I am receiving the database entry all inner class objects are assigned with null and not with the database value.
This is a device:
public class Device {
public String id;
public String serialNumber;
public String name;
public int status;
public String errorReport;
public List<Sensor> sensors=new ArrayList<>();
public List<Action> actions=new ArrayList<>();
public List<String> filterTags= new ArrayList<>();
public List protocols;
}
This is an example entry from the database, as you can see the values are saved well:
{
"_id": "7_openHabsamsungtv:tv:cbaf7d7d_4e10_41e6_9c1d_864988057bda",
"actions": [
{
"_id": "samsungtv:tv:cbaf7d7d_4e10_41e6_9c1d_864988057bda:volume",
"deviceId": "7_openHabsamsungtv:tv:cbaf7d7d_4e10_41e6_9c1d_864988057bda",
"errorReport": "Value wurde nicht initialisiert",
"name": "Lautst�rke",
"state": 0,
"value": 0,
"valueOption": {
"maximum": 0,
"minimum": 0,
"percentage": true
},
"valueable": true
}
],
"filterTags": [],
"name": "[TV] Chine ",
"sensors": [
{
"_id": "samsungtv:tv:cbaf7d7d_4e10_41e6_9c1d_864988057bda:sourceId",
"name": "Source ID"
},
{
"_id": "samsungtv:tv:cbaf7d7d_4e10_41e6_9c1d_864988057bda:programTitle",
"name": "Titel"
},
{
"_id": "samsungtv:tv:cbaf7d7d_4e10_41e6_9c1d_864988057bda:channelName",
"name": "Kanal"
}
],
"status": 0
}
And this is what it looks like when I am receiving it from the database again:
{
"id": "7_openHabsamsungtv:tv:cbaf7d7d_4e10_41e6_9c1d_864988057bda",
"serialNumber": null,
"name": "[TV] Chine ",
"status": 0,
"errorReport": null,
"sensors": [
{
"id": null,
"name": null,
"errorReport": null
},
{
"id": null,
"name": null,
"errorReport": null
},
{
"id": null,
"name": null,
"errorReport": null
}
],
"actions": [
{
"id": null,
"name": null,
"deviceId": null,
"state": 0,
"states": null,
"valueOption": null,
"value": 0,
"errorReport": "Value wurde nicht initialisiert",
"valueable": false
}
],
"filterTags": [],
"protocols": null
}
So when I am pulling the entries from my db collection it sets the values of the Sensor and Action to null. This is my Java Code for receiving a device:
MongoClientURI connectionString = new MongoClientURI(dummy);
MongoClient mongoClient = new MongoClient(connectionString);
CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(),
fromProviders(PojoCodecProvider.builder().automatic(true).build()));
MongoCollection<Device> devices = database.withCodecRegistry(pojoCodecRegistry).getCollection("devices", Device.class);
Device device = devices.find().first();
I am using the standard MongoDB Java Driver.
Could anyone tell me what I am missing here? Thanks in advance.
TL;DR: You need setters and getters per default config.
I wrote this Unit-Test to reproduce the error, but it does not reproduce except when I play around with the Action and Sensor classes. I am using Mongodb 3.4 and Java driver 3.6.
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.junit.Test;
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertNotNull;
public class MongoDeser {
#Test
public void testDeser() {
MongoClientURI connectionString = new MongoClientURI("mongodb://localhost:27017");
MongoClient mongoClient = new MongoClient(connectionString);
MongoDatabase database = mongoClient.getDatabase("sotest");
PojoCodecProvider codecProvider = PojoCodecProvider.builder()
.automatic(true)
.build();
CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(), fromProviders(codecProvider));
MongoCollection<Device> devices = database.withCodecRegistry(pojoCodecRegistry).getCollection("device", Device.class);
Device device = devices.find().first();
assertNotNull(device.getActions());
assertThat(device.getActions().size(), is(1));
assertThat(device.getActions().get(0).getDeviceId(), is("7_openHabsamsungtv:tv:cbaf7d7d_4e10_41e6_9c1d_864988057bda"));
assertThat(device.getStatus(), is(0));
assertThat(device.getName(), is("[TV] Chine "));
}
}
Do you have missing getters and/or setters in your POJOs?
Mongodb Java driver uses reflection for mapping POJOs from BSON. It needs these Getters and Setters per default configuration. If you don't have them, it may behave erratically. In my testing, sometimes it couldn't find a codec and threw an exception, sometimes the fields were just nulled as in your case. My recommendation would be to use annotations instead and give the Java driver a convention for it.
Sensor class
import java.io.Serializable;
public class Sensor implements Serializable
{
private String id;
private String name;
private final static long serialVersionUID = 8244091126694748358L;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Action class
import java.io.Serializable;
public class Action implements Serializable
{
private String id;
private String deviceId;
private String errorReport;
private String name;
private long state;
private long value;
private ValueOption valueOption;
private boolean valueable;
private final static long serialVersionUID = 3493217442158516855L;
public String getId() {
return id;
}
public String getDeviceId() {
return deviceId;
}
public String getErrorReport() {
return errorReport;
}
public String getName() {
return name;
}
public long getState() {
return state;
}
public long getValue() {
return value;
}
public ValueOption getValueOption() {
return valueOption;
}
public boolean isValueable() {
return valueable;
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
public void setId(String id) {
this.id = id;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public void setErrorReport(String errorReport) {
this.errorReport = errorReport;
}
public void setName(String name) {
this.name = name;
}
public void setState(long state) {
this.state = state;
}
public void setValue(long value) {
this.value = value;
}
public void setValueOption(ValueOption valueOption) {
this.valueOption = valueOption;
}
public void setValueable(boolean valueable) {
this.valueable = valueable;
}
}
CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromProviders(new pojoCodecRegistryCodecProvider()),
CodecRegistries.fromProviders(new ActionCodecProvider()),
CodecRegistries.fromProviders(new SensorCodecProvider()));

Converting Json into a DTO array

I have an interesting JSON parsing problem, at least to me since I am doing this for the first time. I have the following sample JSON and I want to map it to equivalent DTOs:
{
"modules":
[
{
"name":"module1",
"shortId":23425,
"pmns":
[
{
"name":"pmn1",
"position":1,
"pmnType":"D3"
},
{
"name":"pmn3",
"position":3,
"pmnType":"R2"
},
{
"name":"pmn7",
"position":5,
"pmnType":"S1"
},
]
},
{
"name":"module2",
"shortId":1572,
"pmns":
[
{
"name":"pmn1",
"position":3,
"pmnType":"D3"
},
{
"name":"pmn12",
"position":35,
"pmnType":"R2"
},
]
}
]
}
This is my ModuleDTO class:
public class ModuleDTO {
private String _name;
private short _shortId;
private PmnDTO[] _pmns;
public String getName() {
return _name;
}
public short getShortId() {
return _shortId;
}
public PmnDTO[] getPmns() {
return _pmns;
}
#JsonProperty("name")
public void setName(String name) {
this._name = name;
}
#JsonProperty("shortId")
public void setShortId(short shortId) {
this._shortId = shortId;
}
#JsonProperty("pmns")
public void setPmns(PmnDTO[] pmns) {
this._pmns = pmns;
}
}
Not copied here but my PmnDTO class is similar, i.e. getters and setters for each property in the pmn object of JSON.
I wrote the following code to try to map it to DTO. The library I am using is com.FasterXml.jackson (version 2.3.1)
// Got the response, construct a DTOs out of it ...
ObjectMapper mapper = new ObjectMapper();
StringReader reader = new StringReader(response); // Json Response
// Convert the JSON response to appropriate DTO ...
ModuleDTO moduleDto = mapper.readValue(reader, ModuleDTO.class);
Obviously, this code didn't work. Can someone tell, how can I map the JSON response to my DTOs, given that "modules" is an array in the JSON and it also contains a variable size array within itself.
Thank You.
(*Vipul)() ;
First of all your JSON is invalid, so I suspect you want this instead:
{
"modules": [
{
"name": "module1",
"shortId": 23425,
"pmns": [
{
"name": "pmn1",
"position": 1,
"pmnType": "D3"
},
{
"name": "pmn3",
"position": 3,
"pmnType": "R2"
},
{
"name": "pmn7",
"position": 5,
"pmnType": "S1"
}
]
},
{
"name": "module2",
"shortId": 1572,
"pmns": [
{
"name": "pmn1",
"position": 3,
"pmnType": "D3"
},
{
"name": "pmn12",
"position": 35,
"pmnType": "R2"
}
]
}
]
}
Then you can use the online conversion from JSON to POJO here and you'll get the follwing result:
-----------------------------------com.example.Example.java-----------------------------------
package com.example;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Generated;
import javax.validation.Valid;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"modules"
})
public class Example {
#JsonProperty("modules")
#Valid
private List<Module> modules = new ArrayList<Module>();
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("modules")
public List<Module> getModules() {
return modules;
}
#JsonProperty("modules")
public void setModules(List<Module> modules) {
this.modules = modules;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
-----------------------------------com.example.Module.java-----------------------------------
package com.example;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Generated;
import javax.validation.Valid;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"name",
"shortId",
"pmns"
})
public class Module {
#JsonProperty("name")
private String name;
#JsonProperty("shortId")
private Integer shortId;
#JsonProperty("pmns")
#Valid
private List<Pmn> pmns = new ArrayList<Pmn>();
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("shortId")
public Integer getShortId() {
return shortId;
}
#JsonProperty("shortId")
public void setShortId(Integer shortId) {
this.shortId = shortId;
}
#JsonProperty("pmns")
public List<Pmn> getPmns() {
return pmns;
}
#JsonProperty("pmns")
public void setPmns(List<Pmn> pmns) {
this.pmns = pmns;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
-----------------------------------com.example.Pmn.java-----------------------------------
package com.example;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Generated;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"name",
"position",
"pmnType"
})
public class Pmn {
#JsonProperty("name")
private String name;
#JsonProperty("position")
private Integer position;
#JsonProperty("pmnType")
private String pmnType;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("position")
public Integer getPosition() {
return position;
}
#JsonProperty("position")
public void setPosition(Integer position) {
this.position = position;
}
#JsonProperty("pmnType")
public String getPmnType() {
return pmnType;
}
#JsonProperty("pmnType")
public void setPmnType(String pmnType) {
this.pmnType = pmnType;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}

Categories

Resources