I'm having trouble finding a way to convert a CSV file which contains some JSON Arrays and JSON objects to Java Pojos using OpenCSV. (I'm open to other 3rd party libraries if they can handle this better.)
Example CSV data:
id, customers
5 [{"name":"bob", "age": 90}]
Example POJO data:
#Data
#NoArgsConstructor
public class Links {
#CsvBindByName
private int id;
#CsvBindByName
private List<Customer> customers;
}
#Data
#NoArgsConstructor
public class Customer {
#CsvBindByName
private String name;
#CsvBindByName
private int age;
}
I feel like a custom convert might be needed something like below. But cannot get it working.
#CsvBindAndSplitByName(elementType= Customer.class, converter = TextToCustomer.class)
#Override
public List<Customer> convertToRead(String value) {
Gson gson = new Gson();
List<Customer> customers;
Type listType = new TypeToken<List<Customer>>() {
}.getType();
contacts = new Gson().fromJson(value, listType);
return customers;
}
#Override
public String convertToWrite(Object value) {
JSONArray t = (JSONArray) value;
return String.format(t.toJSONString());
}
Related
I have this API request payload containing some nested fields:
{
"myId": "studentOne",
"myFirstName": "joe",
"myLastName": "bloggs",
"demoPackages":
[{
"myparts": "https://example.com/myparts/a1234567-5d25-9gf1-23ua-45pb3874265l",
"myPackages": [
"https:/example.com/myPackages/0sk98926-939a-444a-95ta-8eb40125f7r1"
]
}
]
}
I have this corresponding request model DTO:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class DemoRequest {
private String myId;
private String myFirstName;
private String myLastName;
private ArrayList<DemoPackage> demoPackages;
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class DemoPackage{
private String myparts;
private ArrayList myPackages;
}
}
Now, the challenge. When creating a builder object that holds the API request, I am lost as to how to pass the ArrayList fields. I tried this:
public Object createMyPayload(String myId, String myFirstName, String myLastName, ArrayList myparts, ArrayList myPackages) { //not too sure if I am passing myParts and myPackages correctly here
return DemoRequest.builder()
.myId(myId)
.myFirstName(myFirstName)
.myLastName(myLastName)
.releasePackages(myparts)
.releasePackages(myPackages)
.build();
When I call the createMyPayload() from another class to use the builder object, I am getting a compilation error which suggests that my ArrayList fields data type is wrong:
#When("I send a POST request to the endpoint (.*)$")
public void create(String endpoint, String myId, String myFirstName, String myLastName, ArrayList myparts, ArrayList myPackages) {
String id = "studentOne"
String myFirstName = "joe"
String myLastName = "bloggs"
String myParts = "https://example.com/myparts/a1234567-5d25-9gf1-23ua-45pb3874265l";
String myPackages = "https:/example.com/myPackages/0sk98926-939a-444a-95ta-8eb40125f7r1";
demoClass.post(createPayload.createMyPayload(myId, myFirstName, myLastName, myParts, myPackages), endpoint); // myParts and myPackages throw compilation error that data should be Arraylist but when I change to ArrayList, it's asking me to change back to String
How do I correctly pass myParts and myPackages to the lombok builder object and reuse them elsewhere?
This should work.
Note that I have used ArrayList & Array where [] would be better, but you mentioned API using ArrayList. Used List in declarations rather than ArrayList (because that's better practise)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class PackageBuilderDemo {
public static void main(String[] args) {
PackageBuilderDemo packageBuilderDemo = new PackageBuilderDemo();
packageBuilderDemo.createMyPayload("studentOne", "joe", "bloggs", "https://example.com/myparts/a1234567-5d25-9gf1-23ua-45pb3874265l", Arrays.asList(new String[] {"https:/example.com/myPackages/0sk98926-939a-444a-95ta-8eb40125f7r1"}));
}
public DemoRequest createMyPayload(String myId, String myFirstName, String myLastName, String myParts, List<String> myPackages) {
DemoPackage demoPackage = DemoPackage.builder().myparts(myParts).myPackages(myPackages).build();
List<DemoPackage> demoPackages = new ArrayList<>();
demoPackages.add(demoPackage);
return DemoRequest.builder()
.myId(myId)
.myFirstName(myFirstName)
.myLastName(myLastName)
.demoPackages(demoPackages)
.build();
}
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class DemoRequest {
private String myId;
private String myFirstName;
private String myLastName;
private List<DemoPackage> demoPackages;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class DemoPackage{
private String myparts;
private List<String> myPackages;
}
In my Spring project I have several objects that should be serialized to a specific JSON format.
public class Person {
private Integer id;
private String name;
private String height;
private Address address;
}
and
public class Address {
private String street;
private String city;
private String phone;
}
Let assume that Person.height and Address.phone should not appear in the JSON.
The resulting JSON should look like
{
"attributes": ["id", "name", "street", "city"],
"values": [12345, "Mr. Smith", "Main street", "Chicago"]
}
I can create create a standard JSON with an ObjectMapper and some annotations like #JsonProperty and #JsonUnwrapped where I disable some SerializationFeatures. But at the moment I'm not able to create such a JSON.
Is there an easy way to create this JSON? And how would the way back (deserialization) look like?
There are good reasons Jackson doesn't serializes maps in this format. It's less readable and also harder to deserialize properly.
But if you just create another POJO it's very easy to achieve what you want to do:
public class AttributeList {
public static AttributeList from(Object o) {
return from(new ObjectMapper().convertValue(o, new TypeReference<Map<String, Object>>() {}));
}
public static AttributeList from(Map<String, Object> attributes) {
return new AttributeList(attributes);
}
private final List<String> attributes;
private final List<Object> values;
private AttributeList(Map<String, Object> o) {
attributes = new ArrayList<>(o.keySet());
values = new ArrayList<>(o.values());
}
}
I am developing my web application backend using Spring. In particular, my application manages data on soccer teams and their players.
My application interacts with a third party REST API to fetch team and player data.
As for the teams, I created a Team entity as follows:
#Data
#Table(name = "team")
#Entity
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false)
private Long id;
private String name;
private String logoUrl;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "team")
private Set<Player> players;
}
The response that comes to me from the API, however, has a particular structure and contains an array of Teams in the "response" node.
Here is the structure of the response:
{
"get":"teams",
"parameters":{
"league":"135",
"season":"2020"
},
"errors":[
],
"results":20,
"paging":{
"current":1,
"total":1
},
"response":[
{
"team":{
"id":487,
"name":"Lazio",
"country":"Italy",
"founded":1900,
"national":false,
"logo":"https:\/\/media.api-sports.io\/football\/teams\/487.png"
},
"venue":{
"id":910,
"name":"Stadio Olimpico",
"address":"Viale dei Gladiatori, 2 \/ Via del Foro Italico",
"city":"Roma",
"capacity":68530,
"surface":"grass",
"image":"https:\/\/media.api-sports.io\/football\/venues\/910.png"
}
},
{
"team":{
"id":488,
"name":"Sassuolo",
"country":"Italy",
"founded":1922,
"national":false,
"logo":"https:\/\/media.api-sports.io\/football\/teams\/488.png"
},
"venue":{
"id":935,
"name":"MAPEI Stadium - Citt\u00e0 del Tricolore",
"address":"Piazza Azzuri d'Italia, 1",
"city":"Reggio nell'Emilia",
"capacity":23717,
"surface":"grass",
"image":"https:\/\/media.api-sports.io\/football\/venues\/935.png"
}
},
... // Other team objects
]
}
How can I parse the answer to get a List<Team> using the Jackson library?
You should create classes for Jackson that match result structure then convert instances of those classes to your Team class. Using same class for JPA entity and for Jackson deserialization is a bad idea.
There are online services that allow generating classes like this. For example this one https://json2csharp.com/json-to-pojo generated classes like this:
// import com.fasterxml.jackson.databind.ObjectMapper; // version 2.11.1
// import com.fasterxml.jackson.annotation.JsonProperty; // version 2.11.1
/* ObjectMapper om = new ObjectMapper();
Root root = om.readValue(myJsonString), Root.class); */
public class Parameters{
public String league;
public String season;
}
public class Paging{
public int current;
public int total;
}
public class Team{
public int id;
public String name;
public String country;
public int founded;
public boolean national;
public String logo;
}
public class Venue{
public int id;
public String name;
public String address;
public String city;
public int capacity;
public String surface;
public String image;
}
public class Response{
public Team team;
public Venue venue;
}
public class Root{
public String get;
public Parameters parameters;
public List<Object> errors;
public int results;
public Paging paging;
public List<Response> response;
}
As #Sankozi said you can modelize your java pojos for json deserialization.
Then use an ObjectMapper for deserialization like :
ObjectMapper mapper = new ObjectMapper();
CollectionType javaType = mapper.getTypeFactory()
.constructCollectionType(List.class, Response.class);
List<Response> asList = mapper.readValue(jsonArray, javaType);
List<Team> teams = asList.stream()
.flatMap(response -> response.getTeam())
.collect(Collectors.toList());
I have a entity:
#Entity
public class User {
#PrimaryKey
private int uId;
private String uName;
private ArrayList<String> uPets = new ArrayList<>();
public User() {}
public User(int id, String name, List<String> pets){
this.uId = id;
this.uName = name;
this.uPets.addAll(pets);
};
//getters setters removed for brevity
}
And the type converter is:
public class Converters {
#TypeConverter
public static ArrayList<String> fromString(String value) {
Type listType = new TypeToken<ArrayList<String>>() {}.getType();
return new Gson().fromJson(value, listType);
}
#TypeConverter
public static String fromArrayLisr(ArrayList<String> list) {
Gson gson = new Gson();
String json = gson.toJson(list);
return json;
}
}
And the database class contains a line telling about the type converter:
#Database (entities = {User.class},version = 1)
#TypeConverters({Converters.class})
public abstract class UserDB extends RoomDatabase {
public abstract UserDAO userDAO();
}
So the above code all works fine in terms of storing the data to the database tables. However, I would now like to parse some JSON data representing the user data into User classes. The Json format looks like:
{ "uId": "123",
"uName": "John Doe",
"uPets": [ "cat", "dog", "fish", "pizza"]
}
When I try to parse the above Json data using Gson it gives me an error that com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at path $.users[0].uPets
I tried adding a type adapter to my GsonBuilder like:
builder.registerTypeAdapter(ArrayList.class, new Converters())
but that also gives me an error:
java.lang.IllegalArgumentException
at com.google.gson.internal.$Gson$Preconditions.checkArgument($Gson$Preconditions.java:46)
at com.google.gson.GsonBuilder.registerTypeAdapter(GsonBuilder.java:497)
...
any help appreciated, thanks in advance
you problem is that used ' in your uPets arrayList :
change ' to " in 'pizza"
'pizza" --> "pizza"
My question is kind of similar to Prevent GSON from serializing JSON string but the solution there uses GSON library and I am restricted to using Jackson (fasterxml).
I have an entity class as follows:
package com.dawson.model;
import com.dawson.model.audit.BaseLongEntity;
import lombok.extern.log4j.Log4j;
import javax.persistence.*;
#Table(name = "queue", schema = "dawson")
#Entity
#Log4j
public class Queue extends BaseLongEntity {
protected String requestType;
protected String body;
protected Queue() {
}
public Queue(String requestType, String body) {
this.requestType = requestType;
this.body = body;
}
#Column(name = "request_type")
public String getRequestType() {
return requestType;
}
public void setRequestType(String requestType) {
this.requestType = requestType;
}
#Column(name = "body")
#Lob
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
I want to populate the body field with the json string representation of a map and then send this as part of the ResponseEntity. Something as follows:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.writer().withDefaultPrettyPrinter();
HashMap<String, String> map = new HashMap<>(5);
map.put("inquiry", "How Can I solve the problem with Jackson double serialization of strings?");
map.put("phone", "+12345677890");
Queue queue = null;
try {
queue = new Queue("General Inquiry", mapper.writeValueAsString(map));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
String test = mapper.writeValueAsString(map)
System.out.println(test);
Expected Output: "{"requestType": "General Inquiry","body": "{"inquiry":"How Can I solve the problem with Jackson double serialization of strings?","phone":"+12345677890"}"}"
Actual Output:"{"requestType": "General Inquiry","body": "{\"inquiry\":\"How Can I solve the problem with Jackson double serialization of strings?\",\"phone\":\"+12345677890\"}"}"
I am using
Jackson Core v2.8.2
I tried playing with
#JsonIgnore
and
#JsonProperty
tags but that doesn't help because my field is already serialized from the map when writing to the Entity.
Add the #JsonRawValue annotation to the body property. This makes Jackson treat the contents of the property as a literal JSON value, that should not be processed.
Be aware that Jackson doesn't do any validation of the field's contents, which makes it dangerously easy to produce invalid JSON.