I have a spring project and class like that and want to produce json with root name as type. Here is an example:
public class Person {
private String type; //worker
private String name; //Dennis
private String surname; //Ritchie
}
Result should be:
{"worker" : {
"name" : "Dennis" ,
"surname" : "Ritchie"
}
}
Can I do it with Json tags like #JsonRootName or should I write a Class for worker and extend Person class (There are 3 different types)?
You can implement a custom Serializer when you need to serialize a object into a JSON with a different form:
public class PersonSerializer extends JsonSerializer<Person> {
#Override
public void serialize(Person person, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeFieldName(person.getType());
jgen.writeStartObject();
jgen.writeFieldName("name", person.getName());
jgen.writeFieldName("surname", person.getSurname());
jgen.writeEndObject();
jgen.writeEndObject();
}
}
After that, you can register the serializer on the class:
#JsonSerialize(using = PersonSerializer.class)
public class Person {
private String type;
private String name;
private String surname;
}
Related
At the deserialization process (which as I understand is the process of converting JSON data into a Java Object), how can I tell Jackson that when it reads a object that contains no data, it should be ignored?
I'm using Jackson 2.6.6 and Spring 4.2.6
The JSON data received by my controller is as follows:
{
"id": 2,
"description": "A description",
"containedObject": {}
}
The problem is that the object "containedObject" is interpreted as is and it's being instantiated. Therefore, as soon as my controller reads this JSON data, it produces an instance of the ContainedObject object type but I need this to be null instead.
The easiest and fastest solution would be that in the JSON data received, this value be null like this:
{
"id": 2,
"description": "A description",
"containedObject": null
}
But this isn't possible since I'm not in control of the JSON data that is sent to me.
Is there an annotation (like this explained here) that works for the deserialization process and could be helpfull in my situation?
I leave a representation of my classes for more information:
My entity class is as follows:
public class Entity {
private long id;
private String description;
private ContainedObject containedObject;
//Contructor, getters and setters omitted
}
And my contained object class as follows:
public class ContainedObject {
private long contObjId;
private String aString;
//Contructor, getters and setters omitted
}
I would use a JsonDeserializer. Inspect the field in question, determine, if it is emtpy and return null, so your ContainedObject would be null.
Something like this (semi-pseudo):
public class MyDes extends JsonDeserializer<ContainedObject> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
//read the JsonNode and determine if it is empty JSON object
//and if so return null
if (node is empty....) {
return null;
}
return node;
}
}
then in your model:
public class Entity {
private long id;
private String description;
#JsonDeserialize(using = MyDes.class)
private ContainedObject containedObject;
//Contructor, getters and setters omitted
}
Hope this helps!
You can implement a custom deserializer as follows:
public class Entity {
private long id;
private String description;
#JsonDeserialize(using = EmptyToNullObject.class)
private ContainedObject containedObject;
//Contructor, getters and setters omitted
}
public class EmptyToNullObject extends JsonDeserializer<ContainedObject> {
public ContainedObject deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
long contObjId = (Long) ((LongNode) node.get("contObjId")).numberValue();
String aString = node.get("aString").asText();
if(aString.equals("") && contObjId == 0L) {
return null;
} else {
return new ContainedObject(contObjId, aString);
}
}
}
Approach 1 : This is mostly used. #JsonInclude is used to exclude properties with empty/null/default values.Use #JsonInclude(JsonInclude.Include.NON_NULL) or #JsonInclude(JsonInclude.Include.NON_EMPTY) as per your requirement.
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
private String empId;
private String firstName;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String lastName;
private String address;
private String emailId;
}
More info about the jackson annotations : https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations
Approach 2 : GSON
use GSON (https://code.google.com/p/google-gson/)
I'm mapping object (code to which I have no access) into json using Jackson Object Mapper and MixIn class. So, for example i have this object:
public class Person {
private String name;
private String surname;
public Person (String name, String surname) {
this.name = name;
this.surname = surname;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
}
and MixIn class like this:
abstract class MixIn {
#JsonIgnore
abstract String getSurname(); // we don't need this in json output!
}
This working well. But what if i need to change output of "getName()" getter to return like return person.name + " " + person.surname.
Is this even possible to do by specific Object Mapper configuration? (when i have no access to Person class code)
You could write a custom serializer:
class PersonSerializer extends JsonSerializer<Person> {
#Override
public void serialize(Person person, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeStartObject();
generator.writeStringField("name", person.getName() + " " + person.getSurname());
generator.writeEndObject();
}
}
Then, there are multiple ways to register it, for example using a module:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Person.class, new PersonSerializer());
mapper.registerModule(module);
System.out.println(mapper.writeValueAsString(new Person("user", "1827257")));
You may want to serialize the object differently in different cases, so using mixins is preferred:
objectMapper.getSerializationConfig().addMixInAnnotations(Person.class, MixIn.class);
String json = objectMapper.writeValueAsString(new Person("user", "1827257"));
I have such an entity:
public class User {
private Long id;
private String email;
private String password;
private List<Role> roles;
//set of constructors, getters, setters...
and related JSON:
[
{
"email": "user#email",
"roles": ["REGISTERED_USER"]
}
]
I'm trying to deserialize it in Spring MVC #Controller in such way:
List<User> users = Arrays.asList(objectMapper.readValue(multipartFile.getBytes(), User[].class));
Before adding List<Role> it worked perfect, but after I still have no luck. It seems I need some custom deserializer, could you help with approach for solving? Thank you!
If you have access to Role class you can just add such constructor:
private Role(String role) {
this.role = role;
}
or static factory methods:
#JsonCreator
public static Role fromJson(String value){
Role role = new Role();
role.setRole(value);
return role;
}
#JsonValue
public String toJson() {
return role;
}
Otherwise you will have to write custom deserealizer and register it on object mapper like this:
public static class RoleSerializer extends JsonSerializer {
#Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeString(((Role) value).getRole());
}
}
public static class RoleDeserializer extends JsonDeserializer {
#Override
public Role deserialize(JsonParser jsonParser,DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
Role role = new Role();
role.setRole(node.asText());
return role;
}
}
Here is demo: https://gist.github.com/varren/84ce830d07932b6a9c18
FROM: [{"email": "user#email","roles": ["REGISTERED_USER"]}]
TO OBJ: [User{id=null, email='user#email', password='null', roles=Role{role='REGISTERED_USER'}}]
TO JSON:[{"email":"user#email","roles":["REGISTERED_USER"]}]
Ps:
If you use ObjectMapper like this
Arrays.asList(objectMapper.readValue(multipartFile.getBytes(), User[].class));
then code from demo will work, but you will probably have to set custom Bean in Spring for jackson ObjectMapper to make RoleDeserializer and RoleSerializer work everywhere. Here is more info:
Spring, Jackson and Customization (e.g. CustomDeserializer)
I have two class
public class Person {
private long id;
private String name;
private Gender gender;
// getter and setter omitted
}
and
public class Gender {
private long id;
private String value;
// getter and setter omitted
}
By default the JSON mapping with Jackson library of a Person object is:
{
id: 11,
name: "myname",
gender: {
id: 2,
value: "F"
}
}
I'd like to known how to configure Jackson to obtain:
{
id: 11,
name: "myname",
gender: "F"
}
I don't want mapping all the Gender object but only its value property.
You can use a custom serializer:
public class GenderSerializer extends JsonSerializer<Gender> {
public GenderSerializer() {
}
#Override
public void serialize(Gender gender, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(gender.getValue());
}
}
And in your Person class:
public class Person {
private long id;
private String name;
#JsonSerialize(using = GenderSerializer.class)
private Gender gender;
}
you might wanna see this for custom mapping OR if you need a short cut then you can change getter/setter of gender like this
public String getGender(String type){
this.getGender().getValue();
}
public void setGender(String value){
Gender gender = new Gender();
gender.setId(2);
gender.setValue(value);
this.gender = gender;
}
further you can also put condition for setting male/female on value= M/F
No need for custom serializer/deserializer if you can modify Gender class. For serialization you can use #JsonValue, for deserialization simple constructor (optionally annotated with #JsonCreator):
public class Gender {
#JsonCreator // optional
public Gender(String desc) {
// handle detection of "M" or "F"
}
#JsonValue
public String asString() { // name doesn't matter
if (...) return "M";
return "F";
}
}
Alternatively you could use a static factory method instead of constructor; if so, it must be annotated with #JsonCreator.
And return type of method annotated with #JsonValue can be anything that Jackson knows how to serialize; here we use String, but Enum, POJO and such are fine as well.
I am attempting to deserialise a response entity into a list of POJOs. When I do this directly, using a GenericType like so:
private List<UserRole> extractMembersDirectly(final ClientResponse response) {
return response.getEntity(new GenericType<List<UserRole>>() {});
}
I get this exception:
com.sun.jersey.api.client.ClientHandlerException: com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (START_OBJECT), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.util.List)
However, I can deserialise successfully when I use an ObjectMapper directly:
private List<UserRole> extractMembersUsingMapper(final ClientResponse response) throws IOException {
String json = response.getEntity(String.class);
ObjectMapper mapper = new ObjectMapperFactory().build();
return mapper.readValue(json, new TypeReference<List<UserRole>>() {});
}
The POJO is just:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonSnakeCase
public class UserRole {
private UUID id;
public UserRole(#JsonProperty("id") final UUID id) {
this.id = id;
}
public UUID getId() {
return id;
}
Is there a way to directly deserialise from the entity without first deserialising to String?
you can try having a custom serializer on the class itself
Writing the serializer
public class UserRoleSerializer extends JsonSerializer<Item> {
#Override
public void serialize(UserRole userRole, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("id", userRole.id);
jgen.writeEndObject();
}
}
Now registering the serializer to your class
#JsonSerialize(using = UserRoleSerializer.class)
public class UserRole {
...
}
Then Just an idea, not sure if thats what you looking for