I am building my simple rest server with Spring boot. My problem is the following:
In one of the objects that I have to get through API, there is an Optional field:
public class MyObject {
.....
public String description;
public Optional<String> description2;
public MyObject (Object obj) {
description = obj.getDescription();
description2= Optional.ofNullable(obj.getDescription2());
}
......
}
When obj.getDescription2() is null, and retrieve it, in JSON format I got:
{
"description": "Lorem ipsum",
"description2": {
"present": false
}
}
What I have to do, to remove description2 from response if it's not present?
Thanks
you can use #JsonValue and #JsonCreator on methods that will output the data as you wish. for example:
#JsonValue
public String getDescription2() {
return desription2.orElse(null);
}
Related
{
"id" : "25",
"authors" :
[
{
"firstname":"Ayn",
"lastname":"Rand"
},
{
"firstname":"Mark",
"lastname" : "Twain"
}
]
}
Here is my Java class:
public class FavoriteAuthors {
public String id;
public List<Author> authors;
public static class Author {
public String firstname;
public String lastname;
}
}
I have already tried using the following 2 approaches:
FavoriteAuthors favAuthors = Json.fromJson(json, FavoriteAuthors.class);
and
ObjectMapper objectMapper = new ObjectMapper();
FavoriteAuthors favAuthors = objectMapper.readValue(json, FavoriteAuthors.class);
In both the approaches, I receive the following exception:
(no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Could anyone please point me to the right approach here?
I'm handling A JSON sent in POST Request body using:
Controller
#PostMapping
public Library newLibrary(#RequestBody Library newLibrary) {
return libraryRepository.saveAndFlush(newLibrary);
}
Library model
#Column(name = "Name", nullable = false)
private String name;
JSON in Request Body
{
"name" : "testLibrary"
}
But my application won't receive definite JSON Structure in a call, for example, I receive something like this -
{
"names" : ["testLibrary","testLibrary2"],
"anyKey" : "val",
"anykey2" : {"keys":"vals"}
}
So I need to know how can I handle this situation in a single Post mapping function.
You can use #JsonAnySetter annotation from jackson library.
Inside your model:
private Map<String, Object> details;
#JsonAnySetter
public void setDetail(String key, Object value) {
if (null == details) {
this.details = new HashMap<>();
}
this.details.put(key, value);
}
And it will put extra(not defined into model) fields into the map.
Hope this helps!
I have a DTO class that should serve json via a spring-mvc #RestController.
I want to provide different version/views on the same object. Especially, there are fields that are only used in VERSION_1 of the api, and some only in VERSION_2.
Problem: I could add #JsonView for this, but my goal is also to rename those fields. Some fields should actually replace the same name from previous versions.
Example:
public class Person {
#JsonView(View.Version_1.class)
#JsonProperty("name")
private String name; //eg only the firstname
#JsonView(View.Version_2.class)
#JsonProperty("name")
private NameDTO namedto; //now changing to first+last name
static class NameDTO {
private String firstname;
private String lastname;
}
}
#RestController
public class MyServlet {
#GetMapping("/person/{id}")
#JsonView(View.Version_1.class)
public PersonDTO person1(int id) {
//...
}
#GetMapping("/person_new/{id}")
#JsonView(View.Version_2.class)
public PersonDTO person2(int id) {
//...
}
}
So, depending on the view/version, you would get the same json field firstname, but with different content.
In this example, using V1 would give:
{"name": "john"}
Whereas using V2 should result in:
{"name": {"firstname": "john", "lastname": "doe"}}
BUT not with he code above, as jackson complains:
com.fasterxml.jackson.databind.JsonMappingException: Conflicting
getter definitions for property "name".
Is that possible at all?
I found a way using:
https://github.com/jonpeterson/spring-webmvc-model-versioning
Basic idea is to add a custom VersionedModelConverter that is applied on #VersionedModelConverter annotated webservice response classes.
#Configuration
#Import(VersionedModelResponseBodyAdvice.class)
public class SpringMvcVersioningConfiguration {
//register in jackson. spring-boot automatically registers any module beans
#Bean
public Model versioningModel() {
return new VersioningModule();
}
}
#GetMapping
#VersionedResponseBody(defaultVersion = "2.0")
public Person person() {
}
#JsonVersionedModel(currentVersion = "3.0" toPastConverterClass = PersonConverter.class)
public class Person {
}
public class PersonConverter implements VersionedModelConverter {
#Override
public ObjectNode convert(ObjectNode modelData, String modelVersion, String targetModelVersion, JsonNodeFactory nodeFactory) {
Double modelv = Double.valueOf(modelVersion);
Double targetv = Double.valueOf(targetVersion);
//todo if-else based on model version
Object node = modelData.remove("fieldname");
//node.change...
modelData.set("fieldname_renamed", node);
}
}
i got JSON that have object inside
{
"status": "OK",
"info": null,
"data": {
"product": {
"productId": "1",
"productName": "Oreo"
}
}
i parse with
ProductResponse.java
public class ProductResponse {
#SerializedName("status")
public String status;
#SerializedName("info")
public String info;
#SerializedName("data")
public Product data;
}
Product.java
public class Product {
#SerializedName("productId")
public String productId;
#SerializedName("productName")
public String productName;
}
and in reponse i get status = "OK", info = null, and data that is product with nullfields
You've defined ProductResponse#data as a String. In the JSON, it's clearly not a string, it's an object. I know nothing about GSON, but surely that's the problem... Now you've changed the question so that data is defined as Product.
But data isn't a Product, it's an object that has a product property.
It seems like you should have
public class ProductResponseData {
#SerializeName("product")
public Product product;
}
and then change ProductResponse's data to be:
#SerializedName("data")
public ProductResponseData data;
I have a code from a REST API which uses #ResponseBody to return the result, and a MappingJacksonHttpMessageConverter to return it in a JSON format.
It all works well for complex objects.
For primitives like int, boolean and string I get a JSON which does not start with { or [.
This is not a valid JSON.
I was wondering what is the proper way to return just a simple type like that?
Should I encapsulate it in an object such as { Result : true } ?
Thanks
Code sample:
#RequestMapping(
value = "/login",
method = RequestMethod.POST)
#ResponseBody
public boolean Login(String username, String password) {
return authenticationService.authenticate(username, password);
}
This will return just true or false which is an invalid JSON. It should either be encapsulated in an object or an array (if I understand correctly).
It does just return true, or false. And you are correct that is not json.
It can't be json because its not an object, it is simply a primitive, so its fine as is - it will be assigned to a javascript variable in your success handler.
If you return a list of Booleans you get an array :
[true,false,true]
If you must have fully formed json don't return a primitive use a hashmap or custom wrapper object.
public
#ResponseBody
Map<String, Boolean> getTrue() {
Map<String, Boolean> map = new HashMap<String, Boolean>(1){{put("result", Boolean.TRUE);}};
return map;
}
Returning a hashmap is probably the simplest and best way to get the json you require :
{"result":true}
I've found convenient to use
public class ObjWrapper<T> implements Serializable {
private T obj;
public ObjWrapper(T obj) {
this.obj = obj;
}
public T getObj() {
return obj;
}
}
then in controller:
#RequestMapping("/getString") #ResponseBody
public ObjWrapper<String> getString() { ...
and on client (jquery)
$.getJson("getString", {}, function (data) {
alert(data.obj);
})
same with lists:
public class ListWrapper<T> implements Serializable {
private List<T> content;
public ListWrapper(T... objects) {
content = Arrays.asList(objects);
}
public ListWrapper(List<T> content) {
this.content = content;
}
public List<T> getContent() {
return content;
}
}