Deserializing arrays with protostuff - java

I am trying to use protostuff to serialize deserialize json but when i serialize the object the size of the array is put in front
{"id":1,"price":1.2,"name":"alex","tags":{"a":3,"b":["tag1","tag2","tag2"]}}
if i trie to desirialize the same string it works like a charm but my data dosent have "a":3,"b": for the tags its just simple
{"id":1,"price":1.2,"name":"alex","tags":["tag1","tag2","tag2"]}
when i trie to desirialize a string like the above i get an exception thrown
io.protostuff.JsonInputException: Expected token: { but was VALUE_STRING on tags of message java.lang.reflect.Array
java code used:
String[] x = {"tag1", "tag2", "tag2"};
Product t = new Product(1, 1.2, "alex", x);
Path path = Paths.get("...");
byte[] as = Files.readAllBytes(path);
io.protostuff.Schema<Product> schema = RuntimeSchema.getSchema(Product.class);
LinkedBuffer buffer = LinkedBuffer.allocate(512);
byte[] protostuff;
try {
protostuff = JsonIOUtil.toByteArray(t, schema, false , buffer);
} finally {
buffer.clear();
}
// deser
Product f = schema.newMessage();
JsonIOUtil.mergeFrom(as, f, schema,false);
product class:
public class Product {
private int id;
private double price;
private String name;
private String[] tags;
public Product(int id, double price, String name, String[] tags) {
this.id = id;
this.price = price;
this.name = name;
this.tags = tags;
}
public Product() {
}
public int getId() {
return id;
}
public double getPrice() {
return price;
}
public String getName() {
return name;
}
public String[] getTags() {
return tags;
}
public void setId(int id) {
this.id = id;
}
public void setPrice(double price) {
this.price = price;
}
public void setName(String name) {
this.name = name;
}
public void setTags(String[] tags) {
this.tags = tags;
}
#Override
public String toString() {
return name+" "+ price+" "+ id+" "+ Arrays.toString(tags);
}
}

Protostuff uses special schema for serializing arrays - it puts array size to a serialized form for performance reasons.
You should change field type of tags to List:
private List<String> tags;
Lists are serialized directly into a JSON array:
{"id":1,"price":1.2,"name":"alex","tags":["tag1","tag2","tag2"]}

Related

MalformedJsonException while converting JSON data to Java Object

while using Postman to test the #POST method of RESTEasy, I got the error MalformedJsonException
My #POST method
#POST
#Path("service/product")
#Consumes("application/json")
public Object setProductData(Product product) {
String result = product.toString().trim();
Gson gson = new Gson();
Data.addProduct(gson.fromJson(result, Product.class));
return Response.status(200).entity(result).build();
}
My model
public class Product {
private String id;
private String name;
private String image;
private double price;
private String catalogId;
public Product(String id, String name, String image, double price, String catalogId) {
this.id = id;
this.name = name;
this.image = image;
this.price = price;
this.catalogId = catalogId;
}
public Product() {
}
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;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getCatalogId() {
return catalogId;
}
public void setCatalogId(String catalogId) {
this.catalogId = catalogId;
}
#Override
public String toString() {
return "{" +
"id='" + id + ',' +
"name='" + name + ',' +
"image='" + image + ',' +
"price='" + price + ',' +
"catalogId='" + catalogId + ',' + "}";
}
}
This is what I want to add:
https://i.imgur.com/XACBopY.png
The data is in json format, {"id":"band1","name":"Mi Band 4","image":"https://i.imgur.com/7MLMnhW.jpg","price":30.0,"catalogId":"abc1"} for examle
The error:
https://i.imgur.com/8suya35.png
Earlier I got the error Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $ but then I realized toString() method in Product class was the problem, I fixed it and it produced the error in the question.
Please help me to fix this error.
Your toString() is faulty to begin with - the Json formulation isn't correct.
If you want to use toString() anyways to convert your POJO into JSON, use apache commons lang3's JSON style in the toString().
import com.google.gson.Gson;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class Test1 {
public static void main(String[] args) {
Product product = new Product("1", "someone", "https://url", 1.23, "11");
System.out.println(product);
Gson gson = new Gson();
Product product1 = gson.fromJson(product.toString().trim(), Product.class);
System.out.println(product1);
}
private static class Product {
private String id;
private String name;
private String image;
private double price;
private String catalogId;
public Product() {
}
public Product(String id, String name, String image, double price, String catalogId) {
this.id = id;
this.name = name;
this.image = image;
this.price = price;
this.catalogId = catalogId;
}
#Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.JSON_STYLE)
.append("id", id)
.append("name", name)
.append("image", image)
.append("price", price)
.append("catalogId", catalogId)
.toString();
}
public String getId() {
return id;
}
public Product setId(String id) {
this.id = id;
return this;
}
public String getName() {
return name;
}
public Product setName(String name) {
this.name = name;
return this;
}
public String getImage() {
return image;
}
public Product setImage(String image) {
this.image = image;
return this;
}
public double getPrice() {
return price;
}
public Product setPrice(double price) {
this.price = price;
return this;
}
public String getCatalogId() {
return catalogId;
}
public Product setCatalogId(String catalogId) {
this.catalogId = catalogId;
return this;
}
}
}
The output is as follows:-
{"id":"1","name":"someone","image":"https://url","price":1.23,"catalogId":"11"}
{"id":"1","name":"someone","image":"https://url","price":1.23,"catalogId":"11"}
Now, coming to the usage. If you are taking the object as an input itself as a POST request body, then why not simply use 'Data.addProduct(product);'?

Api endpoint that edits one element in list

I am currently building a rest api that lets the user enter a recipe and describe it. I am using spring-boot as backend and angularjs as frontend.
This is springboot recipe file
package com.example.springboot;
import com.example.springboot.Recipe;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
#Entity // This tells Hibernate to make a table out of this class
public class Recipe {
public Recipe(){
}
public Recipe(Integer id, String name, String description, String type, Integer preptime, Integer cooktime, String content, Integer difficulty){
this.id = id;
this.name = name;
this.description = description;
this.type = type;
this.preptime = preptimee;
this.cooktime = cooktime;
this.content = content;
this.difficulty = difficulty;
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String description;
private String type;
private Integer preptime;
#Column(columnDefinition = "TEXT")
private String content;
private Integer difficulty;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getDifficulty() {
return difficulty;
}
public void setDifficulty(Integer difficulty) {
this.difficulty = difficulty;
}
public Integer getpreptime {
return preptime;
}
public void setpreptime(Integer preptime) {
this.preptime = preptime;
}
}
I created an Endpoint where the user can edit the whole recipe. The user can edit the name , description, content and so on in the recipes/edit/{id} endpoint.
The Endpoint looks like this.
#PutMapping("/recipes/edit/{id}")
void updateRecipe(#PathVariable int id, #RequestBody Recipe recipe ) {
System.out.println("entering");
Recipe recipe_ = recipeRepository.findById(id).get();
recipe_.setName(recipe.getName());
recipe_.setDescription(recipe.getDescription());
recipe_.setType(recipe.getType());
recipe_.setpreptime(recipe.getpreptime());
recipe_.setContent(recipe.getContent());
System.out.println("entering " + recipe.getTitle());
System.out.println("entering" + recipe.getType());
System.out.println("entering" + recipe.getDescription());
System.out.println("adding");
recipeRepository.save(recipe_);
}
Now I just want to create an Endpoint which only serves the purpose for renaming the name of the recipe. This putmapping should accept a list as its input then only rename the name of the recipe.
#PutMapping("/recipes/rename")
public List<Recipe> {
System.out.println("entering renaming");
// recipe_.setName(recipe.getName()); ?
}
I don't know how I can implement this. This is what I have come up with so far. An endpoint which takes a list as a parameter.
This is the service.ts file that updates the Recipes in the edit function
service.ts:
updateRecipe (id: number, recipe: any): Observable<any > {
const url = `${this.usersUrl}/edit/${id}`;
return this.http.put(url ,recipe);
}
where the updateRecipe gets called:
save(): void {
const id = +this.route.snapshot.paramMap.get('name');
this.recipeService.updateRecipe2(id, this.recipes)
.subscribe(() => this.gotoUserList());
}
This implementation work , I don't know how I can get it work or how I can rewrite the functions so that it can update only the name of the recipe and not the whole file.
Could someone help me?
Your update the name method should look like that:
#PutMapping("...{id}")
public void updateName(#PathVariable Integer id, #RequestParam String name){
Recipe recipe = repository.findById(id).orElseThrow(...);
recipe.setName(name);
}
if you want to rename list of recipes
public void renameRecipes(String oldName, String newName){
repository.findByName(oldName)
.forEach(r -> r.setName(newName));
}
#PutMapping("recipes/rename")
public void updateNames(#PequestParam String oldName, #RequestParam String newName){
renameRecipes(oldName, newName);
}
Try that.

How to read JSON array values in Spring controller

I would like to iterate Products and get the list of name,code and price and set in my Model class. Any help would be really appreciated - how can I iterate this. When I use obj.get("Products") - it just printing as string - got stuck to iterate.
{
"id": "skd3303ll333",
"Products": [{
"name": "apple",
"code": "iphone-393",
"price": "1939"
},
{
"name": "ipad",
"code": "ipad-3939",
"price": "900"
}
]
}
#PostMapping(path="/create", consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> create(#RequestBody Map<String, Object> obj ) {
System.out.println("Products :" + obj.get("Products"));
}
There are two ways to do this,
1) By type casting (personally i will not prefer this)
List<Map<Object,Object>> productslist = (List<Map<Object, Object>>) obj.get("products");
for(Map entry: productslist) {
for(Object s: entry.keySet()) {
System.out.println(s.toString());
System.out.println(entry.get(s).toString());
}
}
2) Mapping directly to Model class, for this approach you need Jackson library in buildpath
#JsonIgnoreProperties(unknown =true)
public class Customer {
#JsonProperty("id")
private String id;
#JsonProperty("products")
private List<Products> products;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Products> getProducts() {
return products;
}
public void setProducts(List<Products> products) {
this.products = products;
}
}
#JsonIgnoreProperties(unknown =true)
class Products{
#JsonProperty("name")
private String name;
#JsonProperty("code")
private String code;
#JsonProperty("price")
private String price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
Controller
public ResponseEntity<Object> create(#RequestBody Customer obj ) {
You need POJO structure with two classes:
public class Product {
private String name;
private String code;
private int price;
}
public class ProductsGroup {
private long id;
private List<Product> products;
// getters/setters
}
And change your method signature to:
#PostMapping(path="/create", consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ProductsGroup> create(#RequestBody ProductsGroup productGroup)
{
System.out.println("Products :" + productGroup.getProducts());
}
You are trying to process the json using a Map<String, Object> obj, which could be possible in some way, but mostly what you want to do is define a single or multiple POJO classes. These represent the json.
public class IdWrapper {
private String id;
#JsonProperty("Products")
private List<Product> products;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Product> getProducts() {
return products;
}
}
public class Product {
private String name;
private String code;
private String price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
And in you controller like this:
#RestController
#RequestMapping("test")
public class DemoController {
#PostMapping()
public void test(#RequestBody IdWrapper productsWrapper) {
System.out.println();
}
}

Convert pojo (list of object) to json in java using jackson

I'm facing a problem while converting simple JSON to POJO object.
This is my code:
public class Products {
private int id;
private String type;
private String description;
private Double price;
private String brand;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
Im getting below as output:
com.ObjectToJson.Product#b97c004
Object to Json --> {
"products" : null
}
I am not able to convert JSON to/from Java Objects using JACKSON API.
Please, can anyone help me out on this?
Where is the code where you try to create the JSON from your object? Without it, it will be hard to say what to fix. The problem may also be coming from the fact that your Products class has no constructor. So when you make an object its nothing. You should add a constructor after you initialize your variables like:
public Products(){
}
Then try making your JSON from the object, which should be something like:
Products p = new Products();
ObjectMapper mapper = new ObjectMapper();
String JSON = mapper.writeValueAsString(p); // to get json string
mapper.readValue(JSON, Products.class); // json string to obj
Be sure to set your Product's object variables using setters or getters, or to initialize them to values by adding parameters to your constructor:
public Products(String type, String description, Double price,
String brand){
this.type = type;
this.description = description; //etc etc...
}

Intersect and union of two different list of custom objects with streams

I want to get an intersection and union of two Lists of different types. I have been trying using Java 8 streams because I think this is the easiest way to do it. So far I have failed each time.
I have simplified the code so it can easily be reproduced. I have two objects, Data1 and Data2.
For example:
public class Data2 {
private int id;
private String name;
private String type;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Data2(int id, String name, String type) {
this.id = id;
this.name = name;
this.type = type;
}
}
public class Data1 {
private int id;
private String name;
private int amount;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public Data1(int id, String name, int amount) {
this.id = id;
this.name = name;
this.amount = amount;
}
}
public class OutputData {
private int id;
private String name;
private String type;
private int amount;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public OutputData(int id, String name, String type, int amount) {
this.id = id;
this.name = name;
this.type = type;
this.amount = amount;
}
}
They have similar fields.. I need to intersect them based on ID (intersect) and store them in an output (union?) of type OutputData.
Example of a main type:
List<Data2> listOfData2 = new ArrayList<Data2>();
listOfData2.add(new Data2(10501, "JOE" , "Type1"));
listOfData2.add(new Data2(10603, "SAL" , "Type5"));
listOfData2.add(new Data2(40514, "PETER", "Type4"));
listOfData2.add(new Data2(59562, "JIM" , "Type2"));
listOfData2.add(new Data2(29415, "BOB" , "Type1"));
listOfData2.add(new Data2(61812, "JOE" , "Type9"));
listOfData2.add(new Data2(98432, "JOE" , "Type7"));
listOfData2.add(new Data2(62556, "JEFF" , "Type1"));
listOfData2.add(new Data2(10599, "TOM" , "Type4"));
List<Data1> listOfData1 = new ArrayList<Data1>();
listOfData1.add(new Data1(10501, "JOE" ,3000000));
listOfData1.add(new Data1(10603, "SAL" ,6225000));
listOfData1.add(new Data1(40514, "PETER" ,2005000));
listOfData1.add(new Data1(59562, "JIM" ,3000000));
listOfData1.add(new Data1(29415, "BOB" ,3000000));
Here is one of my best attempts at this, with no success and lots of errors:
List<OutputData> od =
listOfData1.stream()
.flatMap(x -> listOfData2.stream()
.filter(y -> x.getId().equals(y.getId()))
.map(y -> new OutputData(x.getId(), x.getName(), y.getType(), x.getAmount()))
.collect(Collectors.toList()));
This should return a List<OutputData> that has one entry of ID 10603, name SAL and all other fields populated.
This should do it, but in the example there are 5 records in each list that have same ids.
List<OutputData> result = listOfData1.stream()
.flatMap(x -> listOfData2.stream()
.filter(y -> x.getId() == y.getId())
.map(y -> new OutputData(y.getId(), x.getName(), y.getType(), x.getAmount())))
.collect(Collectors.toList());
I'll give you some pseudo code:
declare list 3 (type data3)
for i = length of list 1
for j = length of list 2
if list1[i].getid == list2[j].getid
new data3 object with all relevant fields
add to list 3

Categories

Resources