I have this JSON and I want to generate objects with it.
{
"responseId": "response-id",
"session": "projects/project-id/agent/sessions/session-id",
"queryResult": {
"queryText": "End-user expression",
"parameters": {
"orderNumber": "PQL7648194AAAAA",
"lastName": "RIVERO"
},
"action": "order",
"allRequiredParamsPresent": true,
"intent": {
"name": "projects/project-id/agent/intents/intent-id",
"displayName": "[InfoVuelo] Dia-hora de salida - datos de orden"
},
"languageCode": "es"
},
"originalDetectIntentRequest": {}
}
The fields of "parameters" are variable. For example for one situation parameters could be
{
"parameters": {
"email":"example#example.com"
}
}
Also, I could have
{
"parameters": {
"orderNumber": "PQL7648194AAAAA",
"lastName": "RIVERO"
}
}
I made a class Parameters and 2 classes(OrderNumberAndLastNameParams and EmailParams) that extends Parameters
public class EmailParams implements Parameters {
private String email;
}
public class OrderNumberLastNameParams implements Parameters {
private String orderNumber;
private String lastName;
}
My method to create objects with a given JSON file
public static <T> T buildBodyFromJson(String filePath, Class<T> clazz) {
ObjectMapper myMapper = new ObjectMapper();
T jsonMapper = null;
try {
jsonMapper = myMapper.readValue(new ClassPathResource(filePath).getInputStream(), clazz);
} catch (IOException e) {
fail(e.getLocalizedMessage());
}
return jsonMapper;
}
But when I try to build the object, It tries to instantiate Parameter object instead of children objects getting "Unrecognized field ... not marked as ignorable ".
I'm understanding that you don't really need two separated classes, the problem is that you need to generate a requestbody with this object that doesn't have null / empty fields.
Well, there is an annotation that may help you.
Create a single class for paramters with all the 3 properties and annotate with #JsonInclude(JsonInclude.Include.NON_EMPTY)
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Parameters {
private String email;
private String orderNumber;
private String lastName;
}
This annotation makes empty or null fields to not appears on the requestbody that you're creating.
Related
I've tried several ways of storing a json file in a database but it ends up creating different columns for each entry.
I want to store it as a "json" type in a single column.
Is it possible?
My json file.
users.json
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere#april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
},
...
]
It's a spring-boot application and I've the relevant controllers and services.
In my domain package. (Address and Company are Embeddable classes)
User.java
#Data
#AllArgsConstructor #NoArgsConstructor
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String username;
private String email;
private String phone;
private String website;
#Embedded
private Address address;
#Embedded
private Company company;
}
The main file (storing in the database)
TypeReference and ObjectMapper are from Jackson
#SpringBootApplication
public class JsondbApplication {
public static void main(String[] args) {
SpringApplication.run(JsondbApplication.class, args);
}
#Bean
CommandLineRunner runner(UserService service) {
return args -> {
ObjectMapper mapper = new ObjectMapper();
TypeReference<List<User>> reference = new TypeReference<List<User>>() {};
InputStream stream = TypeReference.class.getResourceAsStream("/json/users.json");
try {
List<User> users = mapper.readValue(stream, reference);
service.save(users);
System.out.println("Saved!");
} catch (Exception h) {
System.out.println("Unable to save! " + h.getMessage());
}
};
}
}
in mysql it creates different columns for id, name, username, ...
I want to store it in a single column as a json type using spring boot.
Your code reads json into list of User objects and persists to database. You need to write some custom logic to save it as json. There are multiple ways to do it.
You can do something like
1) Add another variable in User class say private String jsonData
2) In #PrePersist method, write serialization logic
3) Mark other attributes with #JsonInclude() - to include in Jackson
#Transient - to ignore in the persistence in separate column. You might not want to add these annotations to id attribute as each json object will be stored against a specific id in database.
So, new attribute would be something like
#NonNull
#Column(columnDefinition = "JSON") // Only if Database has JSON type else this line can be removed
private String jsonData;
PrePersist:
#PrePersist
public void prePersist() {
try {
this.setJsonData(new ObjectMapper().writeValueAsString(this));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
EDIT:
You might face issue with #Transient attributes being null in the #PrePersist method. In that case you can use a setter method. But you have to call this setter everytime any attribute is changed before the save call.
public void setJsonData(String jsonData) {
// Method parameter jsonData is simply ignored
try {
this.jsonData = new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
log.error(e.getMessage());
}
}
I have created customer json file as below:
[
{
"firstName": “test”,
"lastName": “temp”,
"age": 35,
"emailAddress": “test#Gmail.com",
"address": {
"streetAddress": “test testing“,
"city": “city”,
"postCode": “12343546”,
"state": “state”,
"country": “cy”,
"county": “abc”
},
"phoneNumber": {
"home": "012345678",
"mob": "0987654321"
}
},
{
"firstName": “tug”,
"lastName": “kjk”,
"age": 35,
"emailAddress": “jhgj#Gmail.com",
"address": {
"streetAddress": “jh hjgjhg ,
"city": “kjhjh”,
"postCode": "122345",
"state": “jhgl”,
"country": “jaj”,
"county": “jhgkg”
},
"phoneNumber": {
"home": "012345678",
"mob": "0987654321"
}
}
]
For the Customer JSON data file, I have created below JSON datareader class:
public class JsonDataReader {
private final String customerFilePath = new ConfigFileReader().getTestDataResourcePath() + "Customer.json";
private List<Customer> customerList;
public JsonDataReader(){
customerList = getCustomerData();
}
private List<Customer> getCustomerData() {
Gson gson = new Gson();
BufferedReader bufferReader = null;
try {
bufferReader = new BufferedReader(new FileReader(customerFilePath));
Customer[] customers = gson.fromJson(bufferReader, Customer[].class);
return Arrays.asList(customers);
}catch(FileNotFoundException e) {
throw new RuntimeException("Json file not found at path : " + customerFilePath);
}finally {
try { if(bufferReader != null) bufferReader.close();}
catch (IOException ignore) {}
}
}
public final Customer getCustomerByName(String customerName){
for(Customer customer : customerList) {
if(customer.firstName.equalsIgnoreCase(customerName)) return customer;
}
return null;
}
}
Created POJO class as below:
public class Customer {
public String firstName;
public String lastName;
public int age;
public String emailAddress;
public Address address;
public PhoneNumber phoneNumber;
public class Address {
public String streetAddress;
public String city;
public String postCode;
public String state;
public String country;
public String county;
}
public class PhoneNumber {
public String home;
public String mob;
}
}
This is working fine so far as there is only one JSON data file, however I will create more JSON data files, so may be I have to create multiple POJOs for each one, but is there any way I can write common generic jsondatareader class for all those JSON files?
A class (or an Object) is a well defined entity. By well defined I mean that its structure is known at compile time, and cannot be changed after that point.
Having to create multiple classes to represent multiple JSON documents is perfectly fine. So if you're worried about the amount of files you'll create, it's a non-problem.
But, if the JSON document structure will keep changing along with every request, there is no point in defining a series of classes. To handle totally dynamic JSON you should stick with what Gson offers you. That is JsonElement and its subclasses.
JsonElement
> JsonArray
> JsonObject
> JsonPrimitive
> JsonNull
That's all what is needed to describe a JSON object.
If that is the case then why not convert JSON into a Map instead of a POJO! If you go POJO route then you will utilizing Jackson or GSon heavily in your code base adding bunch of utility methods to iterate over every resulting JSonArray or JSonelements.
In some incoming JSON there is a list
"age" : 27,
"country", USA,
"fields": [
{
"id": 261762251,
"value": "Fred"
},
{
"id": 261516162,
"value": "Dave"
},
]
I know the key int for what I am looking for [261762251].
I would like to map that to a plain String field firstname in the User object with the rest of the bottom level fields from the JSON. I have tried extending com.fasterxml.jackson.databind.util.StdConverter and adding the annotation #JsonSerialize(converter=MyConverterClass.class) to the variable in the User class with no luck.
My architecture is like this:
public class User {
private String age;
private String country;
private String firstname; // this is the field in the list that needs converting
// getters and setters
}
public class ApiClient{
public User getUsers(){
Response response;
//some code to call a service
return response.readEntity(User.class)
}
}
What is the best approach to achieve this?
You can try something like below:
class Tester
{
public static void main(String[] args) throws Exception {
String s1 = "{\"fields\": [ { \"id\": 261762251, \"value\": \"Fred\" }, { \"id\": 261516162, \"value\": \"Dave\" }]}";
ObjectMapper om = new ObjectMapper();
Myclass mine = om.readValue(s1, Myclass.class);
System.out.println(mine);
}
}
public class User {
private String age;
private String country;
private String firstname; // this is the field in the list that needs converting
#JsonProperty("fields")
private void unpackNested(List<Map<String,Object>> fields) {
for(Map<String,Object> el: fields) {
if((Integer)el.get("id") == 261762251) {
firstname = el.toString();
}
}
}
// getters and setters
}
My Spring Boot app makes a call to a REST API and receives a JSON with a varying number of entities. E.g.
{
"content": {
"guest_1": {
"name": {
"firstName": "a",
"lastName": "b"
},
"vip": false
},
"guest_2": {
"name": {
"firstName": "c",
"lastName": "d"
},
"vip": false
},
...more guests omitted...
}
}
There can be 1 to many guests and I don't know their number upfront. As you can see, they aren't in an array, they are objects instead.
I'd like to avoid deserializing into a class like
public class Content {
#JsonProperty("guest_1")
private Guest guest1;
#JsonProperty("guest_2")
private Guest guest2;
// More Guests here each having their own field
}
What I'd like to use is
public class Content {
private List<Guest> guests;
}
The #JsonAnySetter annotation I read about at https://www.baeldung.com/jackson-annotations looks promising but I couldn't get it to work.
3.2. Convert to an object at https://www.baeldung.com/jackson-json-node-tree-model looks also good but it didn't work out either.
I'm not sure if I can make Jackson do this in a declarative way or I should write a custom JsonDeserializer. Could you please help me?
#JsonAnySetter will work as it allows to specify a POJO type as second parameter. You could recreate the example JSON as, omitting setXXX() and getXXX() methods on POJOs for clarity:
private static class Content {
private Guests content;
}
private static class Guests {
private List<Guest> guests = new ArrayList<>();
#JsonAnySetter
private void addGuest(String name, Guest value) {
guests.add(value);
}
}
private static class Guest {
private Name name;
private boolean vip;
}
private static class Name {
private String firstName;
private String lastName;
}
With your JSON example will produce:
Content root = new ObjectMapper().readValue(json, Content.class);
root.getContent().getGuests().stream()
.map(Guest::getName)
.map(Name::getFirstName)
.forEach(System.out::println); // a, c
How do I use fields from current json object and pass it to custom constructor of the next object. If I use default deserialization using gson, it works fine for the fields which have the same name, but since my room pojo has fields streetName and houseName(i want to store which house and street it belongs to), i want to add that while deserializing. How do I do this? I have a json which looks like this:
{
"database": {
"name": "",
"email": "",
"password": "",
"streets": {
"streetName1": {
"houseName1": {
"room1": {
"roomMembers": 3
}
},
"houseName2": {
"room2": {
"roomMembers": 2
}
}
},
"streetName2": {
"houseName3": {
"room3": {
"roomMembers": 1
},
"room4": {
"consumption": ""
}
}
}
}
}
}
My pojos are as follows:
Street.java
public class Street{
String streetName;
List<House> houseList;
///getters and setters
}
House.java
public class House{
String houseName;
String streetName; //this is what I want to set using a different constructor
List<Room> roomList;
///getters and setters
}
Room.java
public class Room{
String roomName;
String houseName;//this is what I want to set using a different
constructor
String streetName; //this is what I want to set using a different
constructor
int roomMembers;
///getters and setters
}