Assign variables from nested JSON string using Jackson - java

Wondering if anyone can help me figure out away to assign the body context to my description String variable.
Here is my JSON string
{"requirement":{"description":{"body":"This is a text"}}}
public class Requirement implements Serializable {
private String description;
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
}
I know I can use #JsonProperty("description") but my description is nested with different context. In this case I only care about the body.

If you don't want to have the class with same structure as the json, you'll have to first unpack the description object and extract body:
public class Requirement {
private String body;
#JsonProperty("description")
private void unpackNested(Map<String,Object> description) {
this.body = (String)description.get("body");
}
}

Your data structure actually looks like this
class Requirement{
private Description description;
}
class Description{
private String body;
}
just add proper #JsonProperty and you will be fine.
In general, every json Object is a separate class (unless you map to plan maps)

Related

What's the best way to add a new Object using the HTTP POST call through Spring?

I'm learning the Spring Framework and I'm struggling with the Rest services with spring, in particular for the POST call that it's supposed to add a new object to the database.
I've seen a lot of different implementations through the web, but I don't know how to pick the best.
Let's take for example a film class:
#Entity
public class Film {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
private String description;
//Constructor, Getter and Setter Omitted.
}
Assuming the repository extends the JpaRepository<Film,Long>, this would be the Controller class:
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
//Implementation #1
#PostMapping("/film")
public Film addNew(#RequestBody Map<String,String> body){
String title = body.get("title");
String description = body.get("description");
return filmRepository.save(new Film(title,description));
}
//Implementation #2
#PostMapping("/film")
public Film addNew(String title, String description){
Film film = new Film(title,description);
System.out.println(film.getTitle() + " " + film.getDescription());
return filmRepository.save(film);
}
//Implementation #3
#PostMapping("/film")
public Film addNew(#RequestBody Film newFilm){
return filmRepository.save(newFilm);
}
}
Why some implementations have as parameter a Map<String, String> ? Is that a body mapped to a key/value pair ?
Also bear in mind that I managed to implement correctly just the implementation #2, the first and the third gave me a
415 error:"Unsupported Media Type" org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=--------------------------901298977805450214809889;charset=UTF-8' not supported]
(Despite I followed the official Spring tutorial) on REST services.
I also read something about the creation of DTO classes where I can define attributes without exposing the object to the controller, how can be implemented such solution?
Implementation 3 is the best practice, but you should create a lightweight DTO class (maybe FilmDto) to avoid exposing the internal structure of your entity, please see LocalDTO, Martin Fowler.
You may use ModelMapper to map FilmDto to Film, and make sure there are proper getters and setters in both classes, if the getters and setters have the same names in both classes, then ModelMapper will do the conversion smoothly:
public class FilmDto {
private long id;
private String title;
private String description;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
and you controller:
#RestController
#RequestMapping("/api")
public class FilmController {
private final FilmRepository filmRepository;
private ModelMapper modelMapper = new ModelMapper();
#Autowired
public FilmController(FilmRepository filmRepository) {
this.filmRepository = filmRepository;
}
//Implementation #3
#PostMapping("/film")
public ResponseEntity<FilmDto> addNew(#RequestBody FilmDto filmDto){
Film newFilm = modelMapper.map(filmDto, Film.class);
newFilm = filmRepository.save(newFilm);
filmDto.setId(newFilm.getId());//you may use modelMapper here
return ResponseEntity.ok(filmDto);
}
}
you can test using postman by passing the film as below:
{
"title": "some title",
"description": "some description"
}
and the body should be of type "raw", "JSON".
Why some implementations have as parameter a Map<String, String> ?
some implementations use map<key,value> because they need the properties that map interface provide such as non-duplicate key value or the classes that implement map interface such as TreeMap and LinkedHashMap.
about your implementation of the class FilmController i think its not necessary to use map<String,String> for posting your domain in the data base simply you can have this implementation
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
#PostMapping("/film")
public ResponseEntity addNew(#RequestBody Film film){
return ResponseEntity.ok(filmRepository.save(film));

How to map single JSON field to multiple JAVA field with GSON?

I'm trying to parse JSON using Retrofit and Gson, but I need to map one JSONfield
's value to multiple JAVA fields inside bean class.
Here is an example code:
class A{
#SerializedName("name");
private String name;
#SerializedName("name");
private String fullName;
}
This is the error I'm seeing: class A declares multiple JSON fields named name. Is there any way to do this?
Update: Please avoid suggesting removing one field from the bean or making changes into getter and setter. The project is huge, and the field is being used later in many other cases, so I don't want to mess with the structure. The question is pretty much clear and on the point.
No need to declare JSON for fullname use name value with fullname in setter gatter.
class A{
#SerializedName("name");
private String name;
private String fullName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFullName() {
return name;
}
public void setFullName(String fullName) {
this.name = fullName;
}
}

Class has two properties of the same name. Why does this happen?

I implemented a Java web service (JAX-RS API Jersey implementation). There is an entity:
#XmlRootElement
public class TestPhoto extends Photo {
public enum Type {
BEFORE, AFTER, ADDON
}
private User author;
#XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
private LocalDateTime createdTime;
private Type type;
public TestPhoto() {
super();
}
public TestPhoto(Long id, String key, String path, User author, LocalDateTime createdTime, Type type) {
super(id, key, path);
this.author = author;
this.createdTime = createdTime;
this.type = type;
}
public User getAuthor() {
return author;
}
public void setAuthor(User author) {
this.author = author;
}
public LocalDateTime getCreatedTime() {
return createdTime;
}
public void setCreatedTime(LocalDateTime createdTime) {
this.createdTime = createdTime;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
}
When I am trying to retrieve a list of such entities, I get an error:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 8 counts of IllegalAnnotationExceptions
Class has two properties of the same name "createdTime"
this problem is related to the following location:
at public java.time.LocalDateTime com.test.TestPhoto.getCreatedTime()
at com.test.TestPhoto
at public java.util.List com.test.TestAccount.getAddonPhotos()
at com.test.TestAccount
this problem is related to the following location:
at private java.time.LocalDateTime com.test.TestPhoto.createdTime
at com.test.TestPhoto
at public java.util.List com.test.TestAccount.getAddonPhotos()
at com.test.TestAccount
I know how to fix this. For instance, I could rename private fields and add _ as a prefix. Or I could add a #XmlAccessorType(XmlAccessType.FIELD) annotation to a field.
I see also, that there is something wrong with #XmlJavaTypeAdapter(LocalDateTimeAdapter.class) annotation. If comment it out everything works fine. But that part of functionality won't work in this case.
What I am trying to understand is why this happened in the first place. The code worked perfectly fine and then just stopped and started throwing such exceptions. Should this code have worked in the first place? Or is it completely wrong?

JSON deserialization issue with jackson

I have a json object like this
{
"id":23 ,
"key": "AKEY",
"description": {
"plain": {
"value": "This is an example",
"representation": "plain"
}
}
}
I'd like to map it to this object
public class JsonResponse {
private int id;
private String key;
private String name;
private String type;
private String description;
/*usual getters and setters*/
}
I use the JSONSerialiser like this
JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON(responseEntity);
But how do I map the "description.plain.value" to "JsonResponse.description"?
Can this be done using jackson annotations?
thanks for your help
I found this post Binding JSON child object property into Java object field in Jackson that partially solved my problem.
I wrote two setDescription() methods, one used by myself in my code, and one that's been called by jacskon
#JsonProperty(value = "description")
public void setDescription(Map<String, Map<String,String>> description) {
this.description = description.get("plain").get("value");
}
public void setDescription(String description) {
this.description = description;
}
It looks like the JsonProperty annotation is required to make jackson use the right setter.
Still I'm ok with this as long as it is a "short nested" property, but I guess Beri response is more acceptable with complex JSON responses.

How to refer to an inner class inside a list in Java

after messing around with parsing a JSON response with GSON for a day, I finally figured out how to get my javabeans right in order to extract my data from the response. This is the layout of my nested classes and lists:
public class LocationContainer {
public class paging {
private String previous;
private String next;
}
private List<Datas> data;
public class Datas {
private String message;
private String id;
private String created_time;
public class Tags {
private List<Data> datas;
public class Data {
private String id;
private String name;
}
}
public class Application {
private String id;
private String name;
}
public class From {
private String id;
private String name;
}
public class Place {
private String id;
private String name;
public class Location {
private int longitude;
private int latitude;
}
}
}
}
Now I am trying to get a hold of the name string inside the place class and the created_time string, but since I am quite a noob, I can't seem to figure it out.
I was able to extract the created_time string by using
String time = gson.toJson(item.data.get(1).created_time);
However using
String name = gson.toJson(item.data.get(1).Place.name);
doesnt work.
The item class is an instance of LocationContainer filled with the output from GSON.
Any pointers would be greatly appreciated.
created_time is a member variable of Data, so your first line is fine.
However, Place is not a member variable, it's just a class definition. You probably need to instantiate a member variable inside your Data class, e.g.:
private Place place;

Categories

Resources