How to bind an array field in java spring framwork? - java

Hi these is my model that inclues some simple field and a set of OrderModel.
public class ActiveRequsetModel {
private Long id;
private String applicatorDescription;
private Customer customer;
private Set<OrderModel> odersModel = new HashSet<>();
}
and this is my method in my controller
#RequestMapping(value = "/register-request", method = RequestMethod.POST, consumes = "application/json")
#ResponseBody
public ResponseEntity<String> registerActiveRequest(#RequestBody ActiveRequsetModel activeRequsetModel){
customerService.registerActiveRequest(activeRequsetModel);
return new ResponseEntity<>("Request registered.", HttpStatus.OK);
}
and this is what I get from client side as you see it includes a applicatorDescription, customer object and a List of orderModel with key ordersModel.
{
"applicatorDescription" : "quick please",
"customer" : {
"id" : 1
},
"ordersModel" :
[
{
"transfereeName" : "Alex",
"address" : "home",
"countOrSize" : 12.5,
"derap" : 4,
"description" : "descriptionnnnn",
"kalite" : {
"id" : 1
},
"product" : {
"id" : 1
}
}
]
}
When I get request spring can bind customer and applicatorDescription, but It can't bind ordersModel.
What should I do?

Your Model class is here:
public class ActiveRequsetModel {
private Long id;
private String applicatorDescription;
private Customer customer;
private Set<OrderModel> oderModel = new HashSet<>();
}
Change private Set<OrderModel> oderModel = new HashSet<>(); as
private List<OrderModel> oderModel
Also, OrderModel class should independent class. If OrderModel is an inner class then jacson cannot bind the request with class.
For more info visit this thread
Hope this will help you

Related

Problem creating compound index in Spring Data MongoDB

Creating index via MongoShell
db.car.createIndex({brand:1 , model:1 , colour:1 ,fuelTypes:1},{unique:true})
Creating CompoundIndex via spring application
#Document
#CompoundIndex(def = "{ 'brand':1 , 'model':1 , 'colour':1 , 'fuelTypes':1 }",unique = true)
public class Car {
private String brand;
private String model;
private List<FuelType> fuelTypes;
private String colour;
}
I was able to create via Mongo shell but not thourgh spring application.What's wrong in the above code?Are n't they equivalent?
I checked After inserting atleast one document.
Thanks in advance.
Here is a working example I tried (creates a new collection, document and the compound index):
The Car POJO class:
#CompoundIndex(name = "car-cmp-idx", def = "{'brand': 1, 'model': 1}", unique = true)
#Document
public class Car {
private String brand;
private String model;
private String colour;
public Car() {
}
public Car(String brand, String model, String colour) {
this.brand = brand;
this.model = model;
this.colour = colour;
}
// get/set methods. etc...
}
The application code to create a document in the (new) car: collection:
MongoOperations ops = new MongoTemplate(MongoClients.create(), "test");
Car car = new Car("Ford", "Model T", "Black");
ops.insert(car);
The result document verified from the mongo shell:
{
"_id" : ObjectId("5ed46f4960c3f13e5edf43b6"),
"brand" : "Ford",
"model" : "Model T",
"colour" : "Black",
"_class" : "com.example.demo.Car"
}
The indexes:
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.car"
},
{
"v" : 2,
"unique" : true,
"key" : {
"brand" : 1,
"model" : 1
},
"name" : "car-cmp-idx",
"ns" : "test.car"
}
]

How to access sub-document in mongoDB with condition in spring-boot program

I want to write a spring-boot program to get values of name, id, and key where abc.active is true. I have written some code
#Repository
public interface SwitchRepoDao extends MongoRepository< SwitchRepo, String> {
public List<SwitchRepo> findByAbc_active(boolean active);
}
also, I have written class for interface.
#Document(collection="switchrepo")
public class SwitchRepo{
#Id
private String id;
private String type;
private List<Abc> abc;
// with getters and setters also constructors.
And Abc is class.
public class Abc{
private String name;
private String id;
private String key;
private boolean active;
This is the code I am using to display output.
#Bean
CommandLineRunner runner(SwitchRepoDao switchRepoDao) {
return new CommandLineRunner() {
#Override
public void run(String... args) throws Exception {
Iterable<SwitchRepo> personList = switchRepoDao.findAllWithStatus(true);
System.out.println("Configuration : ");
for (SwitchRepo config : personList)
{
System.out.println(config.getRegistries().toString());
}
}
};
}
Can anyone please help me with this. For any query related question do comment. Thank You in advance.
Given below is MongoDB Collection from database test. and collection name is switchrepo.
"_id" : "1234567890",
"type" : "xyz",
"abc" : [
{
"name" : "test",
"id" : "test1",
"key" : "secret",
"active" : true
},
{
"name" : "test2",
"id" : "test12",
"key" : "secret2",
"active" : false
}
]
}
In response, I need output as
"id" : "test1",
"key" : "secret",
"active" : true
because active is true in that sub-document array.
Actual Result what I got is "abc" : [{"name" : "test","id" : "test1","key" : "secret","active" : true},{"name" : "test2","id" : "test12","key" : "secret2","active" : false}]
You cannot use property-expressions for a proprety when the the field type is an Array.
here solutions
using the #Query or Aggregations
Solution 1 (Using #Query)
#Repository
public interface SwitchRepoDao extends MongoRepository< SwitchRepo, String> {
//public List<SwitchRepo> findByAbc_active(boolean active);
#Query(value = "{ 'abc.active' : ?0}", fields = "{ 'abc' : 1 }")
List<SwitchRepo> findAllWithStatus(Boolean status);
}
{ 'abc.active' : ?0} for filtring
{ 'abc' : 1 } for only return that part of the document (abc).
Calling findAllWithStatus will return all SwitchRepo with at least one ABC with active is true, you need to filter (using java 8 streams filter for examples all no active Abc from array)
Solution 2 (Using Mongodb aggregation)
Create a new dto class
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection="switchrepo")
public class SwitchRepoDto {
#Id
private String id;
private String type;
private Abc abc;
// with getters and setters also constructors.
public SwitchRepoDto() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Abc getAbc() {
return abc;
}
public void setAbc(Abc abc) {
this.abc = abc;
}
}
Create custom method Add custom method to Repository or inject MongoOperations into your service layer.
#Autowired
private MongoOperations mongoOperations;
public List<SwitchRepoDto> findAllActive() {
UnwindOperation unwind = Aggregation.unwind("$abc");
MatchOperation match = Aggregation.match(Criteria.where("abc.active").is(true));
Aggregation aggregation = Aggregation.newAggregation(unwind,match);
AggregationResults<SwitchRepoDto> results = mongoOperations.aggregate(aggregation, SwitchRepoDto.class, SwitchRepoDto.class);
List<SwitchRepoDto> mappedResults = results.getMappedResults();
return mappedResults;
}

Jackson - Serialize only Ids of objects in a list attribute of a Java Pojo

I want to optimize the json data to be sent on wire. I have three models in my code. These are Customer, Invoice and Particular.
The Customer class
#Data
public class Customer implements Serializable {
private long customerId;
private String name;
private String taxId;
private String phone;
private String address;
private String emailId;
private Date created;
private List<Invoice> invoices;
}
The Invoice class
#Data
public class Invoice implements Serializable {
private String invoiceId;
private List<Particular> particulars;
private Date invoiceDate;
}
The Particular class
#Data
public class Particular {
private String item;
private int quantity;
private float tax;
private int unitPrice;
}
My test code:
#Test
public void makeCustomerJsonWithInvoices() throws JsonProcessingException {
Customer customer = new Customer();
customer.setCustomerId(1234);
customer.setName("Pawan");
customer.setPhone("+918989898989");
customer.setEmailId("something#something.com");
customer.setAddress("Mumbai, India");
customer.setTaxId("MQZ11DPS");
customer.setCreated(new Date());
Invoice invoice1 = new Invoice();
invoice1.setInvoiceId("A-1");
Particular particular1 = new Particular("abc", 1, 0, 12);
Particular particular2 = new Particular("xyz", 2, 0, 20);
invoice1.setInvoiceDate(new Date());
invoice1.setParticulars(Arrays.asList(particular1, particular2));
Particular particular3 = new Particular("mno", 2, 0, 15);
Invoice invoice2 = new Invoice();
invoice2.setInvoiceId("A-2");
invoice2.setParticulars(Arrays.asList(particular3));
invoice2.setInvoiceDate(new Date());
customer.setInvoices(Arrays.asList(invoice1, invoice2));
String value = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
System.out.println(value);
}
What I want here is to avoid the redundancy by serializing the Invoice so that the resulting json would be compact. This should be achieved by only sending the invoiceId attribute value instead of whole Invoice object json.
What the test code prints:
{
"customerId" : 1234,
"name" : "Pawan",
"taxId" : "MQZ11DPS",
"phone" : "+918989898989",
"address" : "Mumbai, India",
"emailId" : "something#something.com",
"created" : 1553243962038,
"invoices" : [ {
"invoiceId" : "A-1",
"particulars" : [ {
"item" : "abc",
"quantity" : 1,
"tax" : 0.0,
"unitPrice" : 12
}, {
"item" : "xyz",
"quantity" : 2,
"tax" : 0.0,
"unitPrice" : 20
} ],
"invoiceDate" : 1553243962038
}, {
"invoiceId" : "A-2",
"particulars" : [ {
"item" : "mno",
"quantity" : 2,
"tax" : 0.0,
"unitPrice" : 15
} ],
"invoiceDate" : 1553243962039
} ]
}
What I want it to print:
{
"customerId" : 1234,
"name" : "Pawan",
"taxId" : "MQZ11DPS",
"phone" : "+918989898989",
"address" : "Mumbai, India",
"emailId" : "something#something.com",
"created" : 1553243962038,
"invoices" : [ {
"invoiceId" : "A-1"
}, {
"invoiceId" : "A-2"
} ]
}
The #Data is lombok annotation used to generate getters and setters.
I tried to add #JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "invoiceId") annotation to Invoice class but this doesn't change the output.
Note that I want this serialization with Invoice happen only when it is passed as a child to a container Model. If I want to send Invoice independently, it shall serialize all fields in Invoice model. I believe this is a common scenario while implementing RESTful WS.
Do I need to write customer serializer for this?
I am able to achieve this by modifying the Customer class in following way.
#Data
public class Customer implements Serializable {
private long customerId;
private String name;
private String taxId;
private String phone;
private String address;
private String emailId;
private Date created;
#JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="invoiceId")
#JsonIdentityReference(alwaysAsId=true)
private List<Invoice> invoices;
}
The answer is inspired from https://stackoverflow.com/a/17583175/1365340
With this I can generate Customer json with invoice Id list. The Invoice object when serialized separately gets all values from all its fields in json.
You can use #JsonIgnore for ingnoring properties in JSON response.
Or you can use transient keyword for avoiding serialization
Consider the following bean as a slight modification of your case:
#Data
#JsonFilter("idOnlyFilter")
#AllArgsConstructor
class Complex {
private String id;
private List<String> aList;
private Date aDate;
}
You could use the #JsonFilter concept to define really granular, on every bean you want, what the conditions for serializing are. Pay especially attention to the filter name idOnlyFilter in the ObjectMapper configuration as well as in the #JsonFilter annotation.
This works as shown below:
#Test
public void includeOnlyOneField() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
FilterProvider filters = new SimpleFilterProvider()
.addFilter("idOnlyFilter", SimpleBeanPropertyFilter.filterOutAllExcept("id"));
Complex complex = new Complex("a string", List.of(), new Date());
// when
String complexAsString = mapper.writer(filters).writeValueAsString(complex);
// then
assertThat(complexAsString).isEqualTo("{\"id\":\"a string\"}");
}
You can use #JsonAutoDetect on Invoice class to serialize only invoiceId field, e.g.:
#JsonAutoDetect(
fieldVisibility = Visibility.NONE,
setterVisibility = Visibility.NONE,
getterVisibility = Visibility.NONE,
isGetterVisibility = Visibility.NONE,
creatorVisibility = Visibility.NONE
)
#Data
public class Invoice implements Serializable {
#JsonProperty (access = READ_ONLY)
private String invoiceId;
private List<Particular> particulars;
private Date invoiceDate;
}
This will make sure only invoiceId goes through the wire, have a look at the documentation here.
Update
If you want to have this behaviour only when Invoice is sent as nested object then you can set the other fields to null (or not set those fields in the first place) and use #JsonInclude annotation, e.g.:
#JsonInclude(Include.NON_NULL)
#Data
public class Invoice implements Serializable {
..
}

Unable to invoke no-args constructor for Product.class

I am Using GSON and Volley library for networking in my android application but while converting the Json response to Model classes using Gson i am getitng the following error:
88-2006/ E/Volley﹕ [121] NetworkDispatcher.run: Unhandled exception java.lang.RuntimeException: Unable to invoke no-args constructor for class [Lcom.example.model.Product;. Register an InstanceCreator with Gson for this type may fix this problem.
java.lang.RuntimeException: Unable to invoke no-args constructor for class [Lcom.example.model.Product;. Register an InstanceCreator with Gson for this type may fix this problem.
these are my POJO classes i am using :
Product.java
public class Product {
private String status;
private List<Result> results = new ArrayList<Result>();
private Pagination pagination;
public Product(){}
// getters and setters
}
Pagination.java
public class Pagination {
private String first;
private String previous;
private String next;
private String last;
public Pagination(){}
}
Result.java
public class Result {
private String id;
private Properties properties;
public Result{}
}
Properties.java
public class Properties {
private String qbcode;
private String name;
private String purchasedate;
private String vendor;
private String thumbnail;
public Properties(){}
}
I have gone through the existing questions which are same like this , as per answers i have found i added the no arg constructor to all the classes but still i am getting the error please help me in solving this issue
The Json String:
{
"status" : "OK",
"results" : [ {
"id" : "IzIzOjE=",
"properties" : {
"qbcode" : "IN-1-1",
"name" : "Test Name",
"purchasedate" : "2015-05-21",
"vendor" : "Test Vendor",
"thumbnail" : "http://en.wikipedia.org/static/images/project-logos/enwiki.png"
}
}, {
"id" : "IzIzOjI=",
"properties" : {
"qbcode" : "IN-1-2",
"name" : "Test Name",
"purchasedate" : "2015-05-21",
"vendor" : "Test Vendor",
"thumbnail" : "http://en.wikipedia.org/static/images/project-logos/enwiki.png"
}
}, {
"id" : "IzIzOjM=",
"properties" : {
"qbcode" : "IN-1-3",
"name" : "Test Name",
"purchasedate" : "2015-05-21",
"vendor" : "Test Vendor",
"thumbnail" : "http://en.wikipedia.org/static/images/project-logos/enwiki.png"
}
},{
"id" : "IzIzOjU=",
"properties" : {
"qbcode" : "IN-1-5",
"name" : "Test Name",
"purchasedate" : "2015-05-21",
"vendor" : "Test Vendor",
"thumbnail" : "http://en.wikipedia.org/static/images/project-logos/enwiki.png"
}
} ],
"pagination" : {
"first" : "/list?size=20",
"previous" : "/list?start=IzIzOjE=&size=20",
"next" : "/list?start=IzIzOjQx&size=20",
"last" : "/list?start=IzIzOjYx&size=20"
}
}
Add default constructor for all classes. Example:
public class Product{
public Product(){
}
}
problem solved, I am doing a foolish call To Gson because i am obtaining a Product class that contains list of other Classes(Result.class etc) but i am sending Product[].class to Gson to convert Hence it thrown exception.
Your model having private attribute you must create setter for the same or create constructor with all parameter like below:
public class Product {
private String status;
private List<Result> results = new ArrayList<Result>();
private Pagination pagination;
public void setStatus(String status) {
this.status = status;
}
public void setResults(List<Result> results) {
this.results = results;
}
public void setPagination(Pagination pagination) {
this.pagination = pagination;
}
}
or
public class Product {
private String status;
private List<Result> results = new ArrayList<Result>();
private Pagination pagination;
public Product(String status, List<Result> results, Pagination pagination) {
this.status = status;
this.results = results;
this.pagination = pagination;
}
}

Spring returns Resource in pure JSON not in HAL Format when including spring data rest

When I use the default controller for my Entities, provided by Spring Data Rest everything works like it should. The output looks like this:
{
"_links" : {
"search" : {
"href" : "http://localhost:8080/users/search"
}
},
"_embedded" : {
"users" : [ {
"firstName" : "Max",
"lastName" : "Mustermann",
"email" : "mail#max-mustermann.de",
"_links" : {
"self" : {
"href" : "http://localhost:8080/users/myadmin"
}
}
} ]
}
}
But if I use my own Controller the output looks like this:
[ {
"firstName" : "Max",
"lastName" : "Mustermann",
"email" : "mail#max-mustermann.de",
"links" : [ {
"rel" : "self",
"href" : "http://localhost:8080/user/myadmin"
} ]
} ]
My Controller
#RestController
#RequestMapping("/user")
#EnableHypermediaSupport(type = {HypermediaType.HAL})
public class UserController {
#Autowired
UserRepository userRepository;
#RequestMapping(method=RequestMethod.GET)
public HttpEntity<List<User>> getUsers(){
ArrayList<User> users = Lists.newArrayList(userRepository.findAll());
for(User user : users){
user.add(linkTo(methodOn(UserController.class).getUser(user.getUsername())).withSelfRel());
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
#RequestMapping(value="/{username}", method= RequestMethod.GET)
public HttpEntity<User> getUser(#PathVariable String username){
User user = userRepository.findByUsername(username);
user.add(linkTo(methodOn(UserController.class).getUser(user.getUsername())).withSelfRel());
return new ResponseEntity<User>(user, HttpStatus.OK);
}
}
My User:
#Entity
#Table(name="users")
public class User extends ResourceSupport{
#Id
private String username;
private String firstName;
private String lastName;
#JsonIgnore
private boolean enabled;
#JsonIgnore
private String password;
#Column(unique = true)
private String email;
public User(){
enabled = false;
}
//Getters and Setters
}
if i delete the spring data rest dependency and include spring-hateoas, spring-plugin-core and json-path (com.jayway.jsonpath) it works.. But i want to use spring-data-rest for some other entities
Two questions:
Why isn't HAL the default with spring data rest included?
How is it possible to set HAL as output format
You need to return a subtype of ResourceSupport (usually Resource or Resources) to let the HAL Jackson converter kick in.
Also, #EnableHypermediaSupport has to be used on a JavaConfig configuration class, not the controller.

Categories

Resources