Showing posts with label jackson. Show all posts
Showing posts with label jackson. Show all posts

Tuesday, October 2, 2018

Deserialize string using custom deserializer specified in field of class

Leave a Comment

I need to write a method that takes some object, some field name fieldName that exists in the given object's class, and some field value value. The value is the JSON-serialized form of the field. That method shall take the value and deserialize it accordingly, something like this:

static void setField(Object obj, String fieldName, String value) throws Exception {     Field field = obj.getClass().getDeclaredField(fieldName)     Object valObj = objectMapper.readValue(value, field.getType());     field.set(obj, valObj); } 

(I actually only need to retrieve the deserialized value, and not set it again, but this makes it a better example.) This works, as long as jackson's default deserialization is sufficient. Now let's assume I have a class with a custom (de)serializer:

class SomeDTO {     String foo;     @JsonSerialize(using = CustomInstantSerializer.class)     @JsonDeserialize(using = CustomInstantDeserializer.class)     Instant bar; } 

One possible solution would be to manually check for JsonDeserialize annotations. However, I really do not want to try to replicate whatever policies Jackson follows to decide what serializer to use, as that seems brittle (for example globally registered serializers).

Is there a good way to deserialize the value using the field's deserialization configuration defined in the DTO class? Maybe deserializing the value into the field's type while passing the field's annotations along to Jackson, so they get honored?

I managed to get a hold of an AnnotatedMember instance, which holds all the required information (JSON-annotations and reflective field- or setter/getter-access), but couldn't figure out how I would use it to deserialize a standalone value due to lack of documentation:

final JavaType dtoType = objectMapper.getTypeFactory().constructType(SomeDTO.class); final BeanDescription description = objectMapper.getDeserializationConfig().introspect(dtoType); for (BeanPropertyDefinition propDef: beanDescription.findProperties()) {     final AnnotatedMember mutator = propertyDefinition.getNonConstructorMutator();     // now what? Also: How do I filter for the correct property? } 

2 Answers

Answers 1

One possibility would be to serialize the object, replace the given field, and then deserialize it again. This can be easily done when serializing from/to JsonNode instead of JSON-String, like this:

static Object setField(Object obj, String fieldName, String value) throws Exception {     // note: produces a new object instead of modifying the existing one     JsonNode node = objectMapper.valueToTree(obj);     ((ObjectNode) node).put(fieldName, value);     return objectMapper.readValue(node.traverse(), obj.getClass()); } 

However, serializing and deserializing a whole object just to deserialize a single field seems like a lot of overhead, and might be brittle because other aspects of the DTO class affect the deserialization process of the single field

Answers 2

import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.ser.std.StdSerializer;  import java.io.IOException; import java.util.Map;  public final class Jackson {    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()       .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);    public static void main(String[] args) throws IOException {     Dto source = makeDto("Master", 31337);     Dto dst = makeDto("Slave", 0xDEADBEEF);      //1. read value of field "fieldName" from json source     //2. clones destination object, sets up field "fieldName" and returns it     //3. in case of no field either on "src" or "dst" - throws an exception     Object result = restoreValue(dst, "details", OBJECT_MAPPER.writeValueAsString(source));     System.out.println(result);   }    private static Object restoreValue(Object targetObject, String fieldName, String sourceObjectAsJson) throws IOException {     String targetObjectAsJson = OBJECT_MAPPER.writeValueAsString(targetObject);     Map sourceAsMap = OBJECT_MAPPER.readValue(sourceObjectAsJson, Map.class);     Map targetAsMap = OBJECT_MAPPER.readValue(targetObjectAsJson, Map.class);     targetAsMap.put(fieldName, sourceAsMap.get(fieldName));     String updatedTargetAsJson = OBJECT_MAPPER.writeValueAsString(targetAsMap);     return OBJECT_MAPPER.readValue(updatedTargetAsJson, targetObject.getClass());   }    private static Dto makeDto(String name, int magic) {     Dto dto = new Dto();     dto.setName(name);     CustomDetails details = new CustomDetails();     details.setMagic(magic);     dto.setDetails(details);     return dto;   }    private static final class Dto {     private String name;     @JsonSerialize(using = CustomDetails.CustomDetailsSerializer.class)     @JsonDeserialize(using = CustomDetails.CustomDetailsDeserializer.class)     private CustomDetails details;      public String getName() {       return name;     }      public void setName(String name) {       this.name = name;     }      public CustomDetails getDetails() {       return details;     }      public void setDetails(CustomDetails details) {       this.details = details;     }      @Override     public String toString() {       return "Dto{" +           "name='" + name + '\'' +           ", details=" + details +           '}';     }   }     private static final class CustomDetails {     private int magic;      public int getMagic() {       return magic;     }      public void setMagic(int magic) {       this.magic = magic;     }      @Override     public String toString() {       return "CustomDetails{" +           "magic=" + magic +           '}';     }      public static final class CustomDetailsSerializer extends StdSerializer<CustomDetails> {        public CustomDetailsSerializer() {         this(null);       }         public CustomDetailsSerializer(Class<CustomDetails> t) {         super(t);       }        @Override       public void serialize(CustomDetails details, JsonGenerator jg, SerializerProvider serializerProvider) throws IOException {         jg.writeStartObject();         jg.writeNumberField("_custom_property_magic", details.magic);         jg.writeEndObject();       }     }       private static final class CustomDetailsDeserializer extends StdDeserializer<CustomDetails> {        public CustomDetailsDeserializer() {         this(null);       }         public CustomDetailsDeserializer(Class<CustomDetails> t) {         super(t);       }        @Override       public CustomDetails deserialize(JsonParser jp, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {         JsonNode node = jp.getCodec().readTree(jp);         int magic = (Integer) node.get("_custom_property_magic").numberValue();         CustomDetails             customDetails = new CustomDetails();         customDetails.setMagic(magic);         return customDetails;       }     }   } } 

so the output is:

Dto{name='Slave', details=CustomDetails{magic=31337}} 
Read More

Wednesday, September 12, 2018

Spring MVC - should my domain classes implement Serializable for over-the-wire transfer?

Leave a Comment

I'm trying to learn Spring Boot by implementing a simple REST API.

My understanding was that if I need to transfer an object over the wire, that object should implement Serializable.

In many examples on the net though, including official ones, domain classes that need to be transferred from server to client (or vice-versa) do not to implement Serializable.

For instance: https://spring.io/guides/gs/rest-service/

But in some cases, they do:

For instance: https://github.com/szerhusenBC/jwt-spring-security-demo/blob/master/src/main/java/org/zerhusen/security/JwtAuthenticationRequest.java

Is there a general rule of thumb on when to implement Serializable?

4 Answers

Answers 1

Using the Java serialization API means you need something in Java on the other side of the wire to deserialize the objects, so you have to control the code that deserializes as well as the code that serializes.

This typically isn't relevant for REST applications, consuming the application response is the business of someone else's code, usually outside your organization. When building a REST application it's normal to try to avoid imposing limitations on what is consuming it, picking a format that is more technology-agnostic and broadly available.

Some reasons for making an object serializable would be:

  • so you can put it in an HttpSession

  • so you can pass it between tiers in a distributed application

  • so you can save it to the file system and restore it later (for instance, you could make the contents of a queue serializable and have the queue contents saved when the application shuts down, reading from the save location when the application starts to restore the queue to its state on shutdown).

In all these cases, you serialize so you can save something to a filesystem or send it across a network.

Answers 2

There are many ways to serialize an object. Java's object serialization is just one of them. From the official documentation:

To serialize an object means to convert its state to a byte stream

REST APIs usually send and receive JSON or XML. In that case serializing an object means converting its state to a String.

There is no direct connection between "sending an object over the wire" and implementing Serializable. The technologies you use dictate whether or not Serializable has to be implemented.

Answers 3

that's a good question when to implement Serializable interface.

these links can provides some useful contents:

Serializing java.io.Serializable instance into JSON with Spring and Jackson JSON

When and why JPA entities should implement Serializable interface?

I sometimes wonder about this,and I think

Because Java is a open source language,and more libraries providered by third party.for tells who will serialize and deserialize the object,the java offical declare a constract interface,makes transfer easy and safety throught different library.

It's just a constract,most third-party libraries can serialize/deserialize when checking implement this constract.and jackson's jar library is not use it.

So you can deem if you use serialize/deserialize object data in your own system,and simple process,likes just serialize and response it(jackson in spring MVC),you needn't to implements it. but if you used in other jar library,likes saving in HttpSession,or other third-party componens/library,you should(or have to) implement Serializable,otherwise the libraries will throw a exception to tell you the constract interfaced which it knows is not provide.

But they said it's a good habit and best properties that to implement the Serializable when serialize a custom class. :)

Answers 4

The specific examples you have mentioned do not transfer objects over the wire. From the example links I see that the controller methods return a domain object with ResponseBody annotation. Just because the return type of the method is the domain object it is not necessary that the whole object is being sent to the client. One of the handler method in Spring mvc framework internally intercepts the invocation and determines that the method return type does not translate to direct ModelAndView object. RequestResponseBoodyMethodProcessor which handles the return value of such annotated methods and uses one of the message converters to write the return object to the http response body. In the case the message converter used would be MappingJackson2HttpMessageConverter. So if are to follow the same coding style you are not required to implement Serializable for your domain objects.

Have a look at this link for the Http message converters provided by default from spring. The list is quiet extensive however not exhaustive and if requirements arise you can implement your own custom message converter to user as-well.

Read More

Wednesday, August 1, 2018

Custom Jackson serializer for a generic tree

Leave a Comment

Say I have a parametrized tree implemented in Java as follows:

public class Tree<E> {    private static class Node {       E element;       List<Node> children.    }     Node root;     //... You get the idea. } 

The idea here is that the implementation above is only concerned with the topology of the tree, but does not know anything about the elements that will be stored in the tree by an instantiation.

Now, say I want my tree elements to be geographies. The reason they are organized in trees is because continents contain countries, countries contain states or a provinces, and so on. For simplicity, a geography has a name and a type:

public class GeoElement { String name; String type; } 

So that, finally, the geo hierarchy looks like so:

public class Geography extends Tree<GeoElement> {} 

Now to Jackson serialization. Assuming the Jackson serializer can see the fields, the direct serialization of this implementation will look like this:

{    "root": {       "element": {          "name":"Latin America",          "type":"Continent"       }       "children": [           {              "element": {                 "name":"Brazil",                 "type":"Country"              },              "children": [                  // ... A list of states in Brazil              ]           },           {              "element": {                 "name":"Argentina",                 "type":"Country"              },              "children": [                  // ... A list of states in Argentina              ]           }       ]    } 

This JSON rendering is no good because it contains the unnecessary artifacts from the Tree and Node classes, i.e. "root" and "element". What I need instead is this:

{    "name":"Latin America",    "type":"Continent"    "children": [        {           "name":"Brazil",           "type":"Country"           "children": [              // ... A list of states in Brazil           ]        },        {           "name":"Argentina",           "type":"Country"           "children": [              // ... A list of states in Argentina           ]        }    ] } 

Any help is most appreciated. -Igor.

4 Answers

Answers 1

What you need is @JsonUnwrapped.

Annotation used to indicate that a property should be serialized "unwrapped"; that is, if it would be serialized as JSON Object, its properties are instead included as properties of its containing Object

Add this annotation to the root field of Tree & element field of Node classes as follows:

public class Tree<E> {    private static class Node {        @JsonUnwrapped       E element;       List<Node> children.    }     @JsonUnwrapped    Node root;     //... You get the idea. } 

And it will give you your desired output:

{     "name": "Latin America",     "type": "Continent",     "children": [{         "name": "Brazil",         "type": "Country",         "children": []     }, {         "name": "Argentina",         "type": "Country",         "children": []     }] } 

Answers 2

Perhaps use @JsonValue like so:

public class Tree<E> {   @JsonValue   Node root; } 

if all you need is to just "unwrap" your tree?

Answers 3

Your best bet will be to build and register a custom serializer for your objects.

Define your serializer:

public class NodeSerializer extends StdSerializer<Node> { 

Then on your Node class:

@JsonSerialize(using = NodeSerializer.class) public class Node { } 

And inside of the NodeSerializer

@Override     public void serialize(       Node node, JsonGenerator jgen, SerializerProvider provider)        throws IOException, JsonProcessingException {          jgen.writeStartObject();         jgen.writeStringField("name", node.element.name);          jgen.writeStringField("type", node.element.type);         //Process children         serializeFields(node, jgen, provider);         jgen.writeEndObject();     } 

This general framework will let you control how the elements get serialized. You may need to @JsonIgnore the element object inside of the Node as well since your custom serializer is taking care of pushing that info into the resulting JSON. There is a lot online about custom serializers and overriding default JSON export.

You can get rid of root in a similar way with a serializer for the Tree implementation.

If you don't want to register the serializer on the class you can also do it on a one at a time basis using the ObjectMapper:

ObjectMapper mapper = new ObjectMapper();  SimpleModule module = new SimpleModule(); module.addSerializer(Node.class, new NodeSerializer()); mapper.registerModule(module);  String serialized = mapper.writeValueAsString(tree); 

The annotation approach will apply globally. This approach allows some control of how/where your custom serializer is used.

Answers 4

For removing the element type, one possibility would be to change your structure so that the name and the type will be directly included in each node:

public class TreeGeo {    private static class Node {       String name;       String type;       List<Node> children.    }     Node root; } 

For removing the root type, I don't know. I suppose that you could extract a sub-object from the jsonObject but I don't know much about Jackson. However, you could give it a better name like world or manipulate the resulting string to remove it manually with some string manipulations.

Read More

Tuesday, July 17, 2018

Spring Boot JacksonTester custom serializer not registered

Leave a Comment

Trying to run the through Jackson library for some upcoming work. I've got the following test model:

Address.java

package com.example.domain;  import java.time.LocalDate;  import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle;  import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;  public class Address {      private String addressKey;     private String postalCode;     private String country;      @JsonSerialize(using = DateSerializer.class, as = String.class)     @JsonDeserialize(using = DateDeserializer.class, as = LocalDate.class)     private LocalDate dateEntered;      public String getAddressKey() {         return addressKey;     }      public void setAddressKey(String addressKey) {         this.addressKey = addressKey;     }      public String getPostalCode() {         return postalCode;     }      public void setPostalCode(String postalCode) {         this.postalCode = postalCode;     }      public String getCountry() {         return country;     }      public void setCountry(String country) {         this.country = country;     }      public LocalDate getDateEntered() {         return dateEntered;     }      public void setDateEntered(LocalDate dateEntered) {         this.dateEntered = dateEntered;     }      @Override     public int hashCode() {         return HashCodeBuilder.reflectionHashCode(this, false);     }      @Override     public boolean equals(Object obj) {         return EqualsBuilder.reflectionEquals(this, obj, false);     }      @Override     public String toString() {         return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE, false);     } } 

and a custom serializer for the dateEntered field:

DateSerializer.java

package com.example.domain;  import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter;  import org.springframework.boot.jackson.JsonComponent;  import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider;  @JsonComponent public class DateSerializer extends JsonSerializer<LocalDate> {     @Override     public void serialize(LocalDate lc, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)             throws IOException, JsonProcessingException {         String date = lc.format(DateTimeFormatter.ofPattern("dd MM yyyy"));         jsonGenerator.writeString(date);     } } 

I've put together the following test, leveraging the @JsonTest annotation to check if the serializer class is doing what i think it should:

AddressTest.java

@JsonTest @RunWith(SpringRunner.class) public class AddressTest {  @Autowired private JacksonTester<Address> json;      @Test     public void test_Address_serialisation_dateEnteredSerialised() throws Exception {         final Address address = getTestAddress();          assertThat(json.write(address))             .hasJsonPathStringValue("@.dateEntered")             .extractingJsonPathStringValue("@.dateEntered")             .isEqualTo("01 05 2018");     }      private Address getTestAddress() {         final Address testAddress = new Address();         testAddress.setAddressKey("testAddressKey");         testAddress.setCountry("testCountry");         testAddress.setPostalCode("testPostalCode");         testAddress.setDateEntered(LocalDate.of(2018, 5, 1));         return testAddress;     }  } 

When i run the serialize test, I get this in console:

com.fasterxml.jackson.databind.JsonMappingException: Invalid definition for property dateEntered (of type 'Lcom/example/domain/Address;'): Can not refine serialization type [simple type, class java.time.LocalDate] into java.lang.String; types not related  at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:284) at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1110) at com.fasterxml.jackson.databind.SerializerProvider.reportBadPropertyDefinition(SerializerProvider.java:1180) at com.fasterxml.jackson.databind.ser.PropertyBuilder.buildWriter(PropertyBuilder.java:101) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._constructWriter(BeanSerializerFactory.java:816) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:610) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanSerializer(BeanSerializerFactory.java:399) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanSerializer(BeanSerializerFactory.java:282) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:234) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:168) at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1308) at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1279) at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:536) at com.fasterxml.jackson.databind.SerializerProvider.findTypedValueSerializer(SerializerProvider.java:743) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:330) at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1427) at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:1158) at com.fasterxml.jackson.databind.ObjectWriter.writeValueAsString(ObjectWriter.java:1031) at org.springframework.boot.test.json.JacksonTester.writeObject(JacksonTester.java:116) at org.springframework.boot.test.json.AbstractJsonMarshalTester.write(AbstractJsonMarshalTester.java:133) at com.example.domain.AddressTest.test_Address_serialisation_dateEnteredSerialised(AddressTest.java:68) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) 

From what I could gather, adding @JsonComponent to the classwould allow it to be picked up and registered when used in conjunction with @JsonTest but based on this it doesnt seem like its being scanned and added. Am i missing a step in the test or src code? Its worth noting that the deserialiser is set up the same way(it toStrings the date in a particular format) and it works fine

1 Answers

Answers 1

Specifying as = ... within @JsonSerialize is rarely needed, especially when you also specify using = ....

Quoted from the javadoc of @JsonSerialize.as:

...
Note: if using is also used it has precedence (since it directly specifies serializer, whereas this would only be used to locate the serializer) and value of this annotation property is ignored.

In your case: Specifying as = String.class seems to be the culprit of the not found JSON serializer.

If you omit it and in your Address class just write

@JsonSerialize(using = DateSerializer.class) @JsonDeserialize(using = DateDeserializer.class) private LocalDate dateEntered; 

then the DateSerializer is found and the AddressTest runs fine without exception.

Read More

Friday, September 15, 2017

Can you configure Spring controller specific Jackson deserialization?

Leave a Comment

I need to add a custom Jackson deserializer for java.lang.String to my Spring 4.1.x MVC application. However all answers (such as this) refer to configuring the ObjectMapper for the complete web application and the changes will apply to all Strings across all @RequestBody in all controllers.

I only want to apply the custom deserialization to @RequestBody arguments used within particular controllers. Note that I don't have the option of using @JsonDeserialize annotations for the specific String fields.

Can you configure custom deserialization for specific controllers only?

5 Answers

Answers 1

Probably this would help, but it ain't pretty. It would require AOP. Also I did not validate it. Create a @CustomAnnotation.

Update your controller:

void someEndpoint(@RequestBody @CustomAnnotation SomeEntity someEntity); 

Then implemment the AOP part:

@Around("execution(* *(@CustomAnnotation (*)))") public void advice(ProceedingJoinPoint proceedingJoinPoint) {   // Here you would add custom ObjectMapper, I don't know another way around it   HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();   String body = request .getReader().lines().collect(Collectors.joining(System.lineSeparator()));    SomeEntity someEntity = /* deserialize */;   // This could be cleaner, cause the method can accept multiple parameters   proceedingJoinPoint.proceed(new Object[] {someEntity}); } 

Answers 2

You can create custom deserializer for your String data.

Custom Deserializer

public class CustomStringDeserializer extends JsonDeserializer<String> {    @Override   public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {      String str = p.getText();      //return processed String   } 

}

Now suppose the String is present inside a POJO use @JsonDeserialize annotation above the variable:

public class SamplePOJO{   @JsonDeserialize(using=CustomStringDeserializer.class)   private String str;   //getter and setter } 

Now when you return it as a response it will be Deserialized in the way you have done it in CustomDeserializer.

Hope it helps.

Answers 3

To have different deserialization configurations you must have different ObjectMapper instances but out of the box Spring uses MappingJackson2HttpMessageConverter which is designed to use only one instance.

I see at least two options here:

Move away from MessageConverter to an ArgumentResolver

Create a @CustomRequestBody annotation, and an argument resolver:

public class CustomRequestBodyArgumentResolver implements HandlerMethodArgumentResolver {    private final ObjectMapperResolver objectMapperResolver;    public CustomRequestBodyArgumentResolver(ObjectMapperResolver objectMapperResolver) {     this.objectMapperResolver = objectMapperResolver;   }    @Override   public boolean supportsParameter(MethodParameter methodParameter) {     return methodParameter.getParameterAnnotation(CustomRequestBody.class) != null;   }    @Override   public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {     if (this.supportsParameter(methodParameter)) {       ObjectMapper objectMapper = objectMapperResolver.getObjectMapper();       HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();       return objectMapper.readValue(request.getInputStream(), methodParameter.getParameterType());     } else {       return WebArgumentResolver.UNRESOLVED;     }   } } 

ObjectMapperResolver is an interface we will be using to resolve actual ObjectMapper instance to use, I will discuss it below. Of course if you have only one use case where you need custom mapping you can simply initialize your mapper here.

You can add custom argument resolver with this configuration:

@Configuration public class WebConfiguration extends WebMvcConfigurerAdapter {    @Bean   public CustomRequestBodyArgumentResolver customBodyArgumentResolver(ObjectMapperResolver objectMapperResolver) {     return new CustomRequestBodyArgumentResolver(objectMapperResolver)   }     @Override   public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {            argumentResolvers.add(customBodyArgumentResolver(objectMapperResolver()));   } } 

Note: Do not combine @CustomRequestBody with @RequestBody, it will be ignored.

Wrap ObjectMapper in a proxy that hides multiple instances

MappingJackson2HttpMessageConverter is designed to work with only one instance of ObjectMapper. We can make that instance a proxy delegate. This will make working with multiple mappers transparent.

First of all we need an interceptor that will translate all method invocations to an underlying object.

public abstract class ObjectMapperInterceptor implements MethodInterceptor {    @Override   public Object invoke(MethodInvocation invocation) throws Throwable {     return ReflectionUtils.invokeMethod(invocation.getMethod(), getObject(), invocation.getArguments());   }     protected abstract ObjectMapper getObject();  } 

Now our ObjectMapper proxy bean will look like this:

@Bean public ObjectMapper objectMapper(ObjectMapperResolver objectMapperResolver) {   ProxyFactory factory = new ProxyFactory();   factory.setTargetClass(ObjectMapper.class);   factory.addAdvice(new ObjectMapperInterceptor() {        @Override       protected ObjectMapper getObject() {         return objectMapperResolver.getObjectMapper();       }    });    return (ObjectMapper) factory.getProxy(); } 

Note: I had class loading issues with this proxy on Wildfly, due to its modular class loading, so I had to extend ObjectMapper (without changing anything) just so I can use class from my module.

It all tied up together using this configuration:

@Configuration public class WebConfiguration extends WebMvcConfigurerAdapter {    @Bean   public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {     return new MappingJackson2HttpMessageConverter(objectMapper(objectMapperResolver()));   }    @Override   public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {     converters.add(jackson2HttpMessageConverter());   } } 

ObjectMapperResolver implementations

Final piece is the logic that determines which mapper should be used, it will be contained in ObjectMapperResolver interface. It contains only one look up method:

public interface ObjectMapperResolver {    ObjectMapper getObjectMapper();  } 

If you do not have a lot of use cases with custom mappers you can simply make a map of preconfigured instances with ReqeustMatchers as keys. Something like this:

public class RequestMatcherObjectMapperResolver implements ObjectMapperResolver {    private final ObjectMapper defaultMapper;   private final Map<RequestMatcher, ObjectMapper> mapping = new HashMap<>();    public RequestMatcherObjectMapperResolver(ObjectMapper defaultMapper, Map<RequestMatcher, ObjectMapper> mapping) {     this.defaultMapper = defaultMapper;     this.mapping.putAll(mapping);   }    public RequestMatcherObjectMapperResolver(ObjectMapper defaultMapper) {     this.defaultMapper = defaultMapper;   }    @Override   public ObjectMapper getObjectMapper() {     ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();     HttpServletRequest request = sra.getRequest();     for (Map.Entry<RequestMatcher, ObjectMapper> entry : mapping.entrySet()) {       if (entry.getKey().matches(request)) {         return entry.getValue();       }     }     return defaultMapper;   }  } 

You can also use a request scoped ObjectMapper and then configure it on a per-request basis. Use this configuration:

@Bean public ObjectMapperResolver objectMapperResolver() {   return new ObjectMapperResolver() {     @Override     public ObjectMapper getObjectMapper() {       return requestScopedObjectMapper();     }   }; }   @Bean @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public ObjectMapper requestScopedObjectMapper() {   return new ObjectMapper(); } 

This is best suited for custom response serialization, since you can configure it right in the controller method. For custom deserialization you must also use Filter/HandlerInterceptor/ControllerAdvice to configure active mapper for current request before the controller method is triggered.

You can create interface, similar to ObjectMapperResolver:

public interface ObjectMapperConfigurer {    void configureObjectMapper(ObjectMapper objectMapper);  } 

Then make a map of this instances with RequstMatchers as keys and put it in a Filter/HandlerInterceptor/ControllerAdvice similar to RequestMatcherObjectMapperResolver.

P.S. If you want to explore dynamic ObjectMapper configuration a bit further I can suggest my old answer here. It describes how you can make dynamic @JsonFilters at run time. It also contains my older approach with extended MappingJackson2HttpMessageConverter that I suggested in comments.

Answers 4

You could try Message Converters. They have a context about http input request (for example, docs see here, JSON). How to customize you could see here. Idea that you could check HttpInputMessage with special URIs, which used in your controllers and convert string as you want. You could create special annotation for this, scan packages and do it automatically.

Note

Likely, you don't need implementation of ObjectMappers. You can use simple default ObjectMapper to parse String and then convert string as you wish. In that case you would create RequestBody once.

Answers 5

You can define a POJO for each different type of request parameter that you would like to deserialize. Then, the following code will pull in the values from the JSON into the object that you define, assuming that the names of the fields in your POJO match with the names of the field in the JSON request.

ObjectMapper mapper = new ObjectMapper();  YourPojo requestParams = null;  try {     requestParams = mapper.readValue(JsonBody, YourPOJO.class);  } catch (IOException e) {     throw new IOException(e); } 
Read More

Sunday, August 27, 2017

Jackson xml module: deserializing immutable type with a @JacksonXmlText property

Leave a Comment

I want to serialize an immutable type both as json and as xml:

The serialized JSON is like:

{     "text" : "... the text..." } 

and the serialized xml is like:

 <asText>_text_</asText> 

(note that the text is the xml's element text)

The java object is like:

@JsonRootName("asText") @Accessors(prefix="_") public static class AsText {      @JsonProperty("text") @JacksonXmlText     @Getter private final String _text;      public AsText(@JsonProperty("text") final String text) {         _text = text;     } } 

beware that the _text property is final (so the object is immutable) and it's annotated with @JacksonXmlText in order to be serialized as the xml element's text

Being the object immutable, a constructor from the text is needed and the constructor's argument must be annotated with @JsonProperty

    public AsText(@JsonProperty("text") final String text) {         _text = text;     } 

When serializing / deserializing to/from JSON everything works fine ... the problem arises when serializing / deserializing to/from XML:

 // create the object  AsText obj = new AsText("_text_");   // init the mapper  XmlMapper mapper = new XmlMapper();   // write as xml  String xml = mapper.writeValueAsString(obj);  log.warn("Serialized Xml\n{}",xml);   // Read from xml  log.warn("Read from Xml:");  AsText objReadedFromXml = mapper.readValue(xml,                                               AsText.class);  log.warn("Obj readed from serialized xml: {}",           objReadedFromXml.getClass().getName()); 

The exception is:

     com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "" (class r01f.types.url.UrlQueryStringParam), not marked as ignorable (2 known properties: "value", "name"]) 

It seems that the xml module needs the object's constructor to be annotated like:

    public AsText(@JsonProperty("") final String text) {         _text = text;     } 

BUT this does NOT even works:

     com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `test.types.SerializeAsXmlElementTextTest$AsText` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator) 

The annotation @JsonProperty("text") at the constructor's argument is needed to deserialize from JSON

... how can i make this to work

3 Answers

Answers 1

Try adding a public getter for the property. I believe that should fix the deserialization issue.

@JsonRootName("asText") @Accessors(prefix = "_") public static class AsText {      @JsonProperty("text")     @JacksonXmlText     @Getter     private final String _text;      public AsText(@JsonProperty("text") final String text) {         _text = text;     }      public String getText() {         return _text;     } } 

Actually, it works without adding a getter too, with these versions of Lombak & Jackson.

<dependencies>     <dependency>         <groupId>com.fasterxml.jackson.dataformat</groupId>         <artifactId>jackson-dataformat-xml</artifactId>         <version>2.9.0</version>     </dependency>      <dependency>         <groupId>org.projectlombok</groupId>         <artifactId>lombok</artifactId>         <version>1.16.18</version>     </dependency> </dependencies> 

Answers 2

I had same issue with error "no delegate- or property-based Creator". In my case it was problem with version 2.5.6. I have fixed it by downgrade to version 2.5.5. Version 2.5.6 is available in mvnrepository.com but on official page is as stable version marked 2.5.5.

Answers 3

It looks like the main reason for your issue is that you try to use JSON and XML serialization at the same time but with different configurations. Unfortunately XmlMapper inherits from ObjectMapper and inherits all the JSON-specific configuration (and you can override it but can not clear it with XML-specific annotations) which is the reason for your conflict. It seems that the simplest way to work this around is to use @JsonAlias annotation in the constructor. It is a bit hacky but it works. Here is a minimal example (without Lombok) that works for me:

@JsonRootName("asText") public static class AsText {      @JsonProperty("text")     @JacksonXmlText     private final String _text;      public AsText(@JsonAlias("") @JsonProperty("text") final String text) {         _text = text;     }      @JsonIgnore     public String getText() {         return _text;     }      @Override     public String toString() {         return "AsText{" +                 "_text='" + _text + '\'' +                 '}';     } } 

Note that I also added @JsonIgnore to the getter because else I didn't get XML format you requested (and you can do the same using Lombok's onMethod as described at onX).

For a simple test:

public static void main(String[] args) throws IOException {     // create the object     AsText obj = new AsText("123_text_");      // init the mapper     //ObjectMapper mapper = new ObjectMapper();     XmlMapper mapper = new XmlMapper();      // write as xml     String xml = mapper.writeValueAsString(obj);     System.out.println("Serialized Xml\n" + xml);      // Read from xml     AsText objReadedFromXml = mapper.readValue(xml, AsText.class);     System.out.println("Read from Xml: " + objReadedFromXml); } 

I get the following output:

Serialized Xml
<asText>123_text_</asText>
Read from Xml: AsText{_text='123_text_'}

Read More

Tuesday, July 18, 2017

Jackson @JsonFilter is not getting applied when used at field or method level

Leave a Comment

I am using Spring version 4.3.3 and Jackson version 2.8.3. I am trying to filter out specific fields from an entity bean based on some custom logic that is determined at runtime. The @JsonFilter seems ideal for this type of functionality. The problem is that when I put it at the field or method level, my custom filter never gets invoked. If I put it at the class level, it gets invoked just fine. I don't want to use it at the class level though since then I would need to separately maintain the list of hardcoded field names that I want to apply the logic to. As of Jackson 2.3, the ability to put this annotation at the field level is supposed to exist.

Here is the most basic custom filter without any custom logic yet:

public class MyFilter extends SimpleBeanPropertyFilter {  @Override protected boolean include(BeanPropertyWriter beanPropertyWriter) {     return true; }  @Override protected boolean include(PropertyWriter propertyWriter) {     return true; } 

}

Then I have the Jackson ObjectMapper configuration:

public class MyObjectMapper extends ObjectMapper {      public MyObjectMapper () {         SimpleFilterProvider filterProvider = new SimpleFilterProvider();         filterProvider.addFilter("myFilter", new MyFilter());         setFilterProvider(filterProvider);     } } 

Then finally I have my entity bean:

@Entity public class Project implements Serializable {      private Long id;     private Long version;     @JsonFilter("myFilter") private String name;     @JsonFilter("myFilter") private String description;      // getters and setters  } 

If I move the @JsonFilter annotation to the class level where @Entity is, the filter at least gets invoked, but when it is at the field level like in the example here, it never gets invoked.

2 Answers

Answers 1

I have the same need but after examining the unit tests I discovered that this is not the use-case covered by annotating a field.

Annotating a field invokes a filter on the value of the field not the instance containing the field. For example, imagine you have to classes, A and B, where A contains a field of type B.

class A {     @JsonFilter("myFilter") B foo; } 

Jackson applies "myFilter" to the fields in B not in A. Since your example contains fields of type String, which has no fields, Jackson never invokes your filter.

Answers 2

You can try this approach for the same purpose:

@Entity @Inheritance(     strategy = InheritanceType.SINGLE_TABLE ) @DiscriminatorColumn(     discriminatorType = DiscriminatorType.STRING,     length = 2 ) @Table(     name = "project" ) @JsonTypeInfo(     use = Id.CLASS,     include = As.PROPERTY,     property = "@class" ) @JsonSubTypes({     @Type(         value = BasicProject.class,         name = "basicProject"     ),     @Type(         value = AdvanceProject.class,         name = "advanceProject"     )}) public abstract class Project {     private Long id;     private Long version;  }  @Entity @DiscriminatorValue("AD") public class AdvanceProject extends Project {     private String name;     private String description; }  @Entity @DiscriminatorValue("BS") public class BasicProject extends Project {     private String name; } 
Read More

Thursday, July 13, 2017

Marshalling two similar json fields to the same java field

Leave a Comment

I have a sample dummy JSON response that looks like :

    {         "id": 1,         "teacher_name": "Foo",         "teacher_address": "123 Main St.",         "teacher_phone_num": 1234567891,         "student_name": "Bar",         "student_address": "546 Main St.",         "student_phone_num": 9184248576     } 

The above is a silly example, but it helps illustrate the issue I am having trying to de-serialize the above into a Java class called "Employee" using Jackson:

public class Employee {     String name;     String address;     String phoneNumber; } 

The issue is that the JSON has two different prepends so I cannot annotate each field in Employee and have the object mapper map teacher_name and student_name to the name field in an Employee object. Is there a way in Jackson to specify two differently named nodes to map to the same Java field?

4 Answers

Answers 1

So in my example, I should end up with two Employee objects (I am guaranteed to have one pair per response)

It is not possible with Jackson. It is designed to map one-to-one: one json object to one java object. But you want to end up with two java objects from one json.

I would recommend you to go with strait forward way by implementing some processing level that will consume Response and map it to two Employee objects.

Answers 2

I think you need to write code to do the mapping whether you use annotation or not.

Simple is the best. If you read the json as JsonNode it should be trivial to code the assignments.

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException;  public class Q44094751 {     public static void main(String[] args) throws IOException {       String json = "{\"id\": 1," +             "\"teacher_name\": \"Foo\", \"teacher_address\": \"123 Main St.\", \"teacher_phone_num\": 1234567891," +             "\"student_name\": \"Bar\", \"student_address\": \"546 Main St.\", \"student_phone_num\": 9184248576" +          "}";       ObjectMapper mapper = new ObjectMapper();       // Read       JsonNode node = mapper.readValue( json, JsonNode.class);       Employee teacher = new Employee(),                student = new Employee();       teacher.name = node.get( "teacher_name" ).toString();       teacher.address = node.get( "teacher_address" ).toString();       teacher.phoneNumber = node.get( "teacher_phone_num" ).toString();       student.name = node.get( "student_name" ).toString();       student.address = node.get( "student_address" ).toString();       student.phoneNumber = node.get( "student_phone_num" ).toString();       // Check       mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);       System.out.println( mapper.writeValueAsString( teacher ) );    }     public static class Employee {       String name;       String address;       String phoneNumber;    } } 

Given a "silly example", the answer that may looks silly. For example if you have a hundred properties, a loop with reflection should work better.

If you have requirement(s) that is not reflected in the example, please edit your question to better describe the actual problem(s) you are facing.

Answers 3

I believe that it is not possible with Jackson, The workarounds that I can think of are

  1. Split it into 2 object Teacher and Student. You can still pass the same object twice but with different classes Teacher and Student, It works, but what about the id field?

  2. Make a Java class similar to JSON.

  3. If you would like to make it more meaning full structure then use this structure

    { "id": 1, "teacher" :{ "name": "Foo", "address": "123 Main St.", "phone_num": 1234567891, }, "student" :{ "name": "Bar", "address": "546 Main St.", "phone_num": 9184248576, } }

Answers 4

You may use @JsonUnwrapped annotation to unwrap the employee's properties inline in parent object. This annotation also provides the option to specify the prefix name of which will be used while ser/der the object.

Below is your example:

 public static class Employee {         private String name;         private String address;         @JsonProperty("phone_num")         private String phoneNumber;          // setter / getter removed from brevity     }      public static class YourJsonObject {         private int id;         @JsonUnwrapped(prefix="teacher_")         private Employee teacher;         @JsonUnwrapped(prefix = "student_")         private Employee student;          // setter / getter removed from brevity     } 

Now Configure the ObjectMapper to use the appropriate naming strategy( from your example,I see snake case strategy is desired)

Test Case:

   @Test     public void peformTest() throws Exception {         final String inputJson = "{\n" +                 "        \"id\": 1,\n" +                 "        \"teacher_name\": \"Foo\",\n" +                 "        \"teacher_address\": \"123 Main St.\",\n" +                 "        \"teacher_phone_num\": 1234567891,\n" +                 "        \"student_name\": \"Bar\",\n" +                 "        \"student_address\": \"546 Main St.\",\n" +                 "        \"student_phone_num\": 9184248576\n" +                 "    }";          ObjectMapper mapper = new ObjectMapper();         // important one         mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);          // read your json as object         YourJsonObject myObject = mapper.readValue(inputJson, YourJsonObject.class);          assertThat(myObject.getTeacher().getName(), is("Foo"));         assertThat(myObject.getStudent().getName(), is("Bar"));     } 

Hope this helps.

Read More

Saturday, May 27, 2017

Validate Json Request Schema

Leave a Comment

So I have this Controller class that contain this method:

    @RequestMapping(value = "/x", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)     public ResponseEntity<MyRepsonseClass> get(         @ApiParam(value = "x", required = true) @Valid @RequestBody MyRequestClass request     ) throws IOException {         //yada yada my logic here         return something;     } 

The Json Request gets automatically Mapped to MyRequestClass.java

This is what that class looks like:

@lombok.ToString @lombok.Getter @lombok.Setter @JsonInclude(JsonInclude.Include.NON_EMPTY) @ApiModel(description = "description") public class MyRequestClass {     private List<SomeClass> attribute1;     private SomeOtherClass attribute2;     private YetAnotherClass attribute3; } 

This is an example of a valid json request:

{     "attribute1": [         {             "key":"value"         }     ],     "attribute3:" {         "key":"value"     } } 

Now, my requirement is to return an Error Message when the request contains an attribute that doesn't exist in the MyRequestClass.java.

As such:

{     "attribute1": [         {             "key":"value"         }     ],     "attribute_that_doesnt_exist:" {         "key":"value"     } } 

Right now it's not throwing any error. Rather, it's simply not mapping that attribute to anything. Are there annotations I can utilize that can make this happen quickly ?? Thank you.

2 Answers

Answers 1

Create a custom deserializer:

public class MyRequestClassDeserializer extends JsonDeserializer<MyRequestClass> {     @Override     public MyRequestClass deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {         MyRequestClass mrc = new MyRequestClass();         ObjectMapper mapper = new ObjectMapper();         JsonToken currentToken = null;         while((currentToken = jsonParser.nextValue()) != null) {             if(currentToken.equals(JsonToken.END_OBJECT)                      || currentToken.equals(JsonToken.END_ARRAY))                 continue;             String currentName = jsonParser.getCurrentName();             switch(currentName) {                 case "attribute1":                     List<SomeClass> attr1 = Arrays.asList(mapper.readValue(jsonParser, SomeClass[].class));                     mrc.setAttribute1(attr1);                     break;                 case "attribute2":                     mrc.setAttribute2(mapper.readValue(jsonParser, SomeOtherClass.class));                     break;                 case "attribute3":                     mrc.setAttribute3(mapper.readValue(jsonParser, YetAnotherClass.class));                     break;                 // <cases for all the other expected attributes>                 default:// it's not an expected attribute                     throw new JsonParseException(jsonParser, "bad request", jsonParser.getCurrentLocation());             }         }         return mrc;     } }   

And add this annotation to your MyRequestClass class: @JsonDeserialize(using=MyRequestClassDeserializer.class)

The only "problem" is that manually deserializing jsons can be a hassle. I would have written the complete code for your case but I'm not good enough at it right now. I might update the answer in the future.

Edit: Done, now it's working code. I thought it was more complicated.

Answers 2

Did you try this annotation @JsonIgnoreProperties(ignoreUnknown = false)

If Spring boot, even better, use this property in application.properties(yaml)

spring.jackson.deserialization.fail-on-unknown-properties=true 
Read More

Sunday, February 12, 2017

Jackson: referencing an object as a property

Leave a Comment

In my java spring application, I am working with hibernate and jpa, and i use jackson to populate data in DB.

Here is the User class:

@Data @Entity public class User{      @Id     @GeneratedValue     Long id;      String username;     String password;     boolean activated;      public User(){} } 

and the second class is:

@Entity @Data public class Roles {      @Id     @GeneratedValue     Long id;      @OneToOne     User user;      String role;      public Roles(){}   } 

In the class Roles i have a property of User and then i made a json file to store the data:

[ {"_class" : "com.example.domains.User", "id": 1, "username": "Admin", "password": "123Admin123","activated":true} ,   {"_class" : "com.example.domains.Roles", "id": 1,"user":1, "role": "Admin"}] 

Unfortunately, when i run the app it complains with:

.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.example.domains.User: no int/Int-argument constructor/factory method to deserialize from Number value (1)  at [Source: N/A; line: -1, column: -1] (through reference chain: com.example.domains.Roles["user"]) 

The problem comes from

{"_class" : "com.example.domains.Roles", "id": 1,"user":1, "role": "Admin"} 

and when i remove the above line the app works well.

I think, it complains because it cannot make an instance of user. So, how can i fix it?

6 Answers

Answers 1

Do yourself a favor and stop using your Entities as DTOs!

JPA entities have bidirectional relations, JSON objects don't, I also believe that the responsibilities of an Entity is very different from a DTO, and although joining these responsibilities into a single Java class is possible, in my experience it is a very bad idea.

Here are a couple of reasons

  • You almost always need more flexibility in the DTO layer, because it is often related to a UI.
  • You should avoid exposing primary keys from your database to the outside, including your own UI. We always generate an additional uniqueId (UUID) for every publicly exposed Entity, the primary key stays in the DB and is only used for joins.
  • You often need multiple views of the same Entity. Or a single view of multiple entities.
  • If you need to add a new entity to a relation with an existing, you will need find the existing one in the database, so posting the new and old object as a single JSON structure has no advantage. You just need the uniqueId of the existing, and then new.

A lot of the problems developers have with JPA, specifically with regards to merging comes from the fact that they receive a detached entity after their json has been deserialized. But this entity typically doesn't have the OneToMany relations (and if it does, it's the parent which has a relation to the child in JSON, but in JPA it is the child's reference to the parent which constitutes the relationship). In most cases you will always need to load the existing version of the entity from the database, and then copy the changes from your DTO into the entity.

I have worked extensively with JPA since 2009, and I know most corner cases of detachment and merging, and have no problem using an Entity as a DTO, but I have seen the confusion and types of errors that occur when you hand such code over to some one who is not intimately familiar with JPA. The few lines you need for a DTO (especially since you already use Lombok), are so simple and allows you much more flexibility, than trying to save a few files and breaking the separation of concerns.

Answers 2

I have changed the json file to :

[   {"_class" : "com.example.domains.User",    "id": 1,    "username": "Admin",    "password": "123Admin123",    "activated":true   },   {     "_class" : "com.example.domains.Roles",     "id": 1,     "user":{"_class" : "com.example.domains.User",             "id": 1,             "username": "Admin",             "password": "123Admin123",             "activated":true           },     "role": "Admin"   } ] 

But i still think, the best ways is using a foreign key to user record. Any solution is welcomed

Answers 3

If your bean doesn't strictly adhere to the JavaBeans format, Jackson has difficulties.

It's best to create an explicit @JsonCreator constructor for your JSON model bean, e.g.

class User {     ...     @JsonCreator    public User(@JsonProperty("name") String name,                 @JsonProperty("age") int age) {             this.name = name;             this.age = age;    }     .. } 

Answers 4

1-1 mapping of fields works well , but when it comes to complex object mapping , better to use some API. You can use Dozer Mapping or Mapstruct to map Object instances. Dozer has spring integration also.

Answers 5

You could specify non default constructors and then use a custom deserialiser.

Something like this should work (it has not been tested).

public class RolesDeserializer extends StdDeserializer<Roles> {       public RolesDeserializer() {          this(null);      }       public RolesDeserializer(Class<?> c) {          super(c);      }      @Override     public Roles deserialize(JsonParser jp, DeserializationContext dsctxt)        throws IOException, JsonProcessingException {         JsonNode node = jp.getCodec().readTree(jp);         long id = ((LongNode) node.get("id")).longValue();         String roleName = node.get("role").asText();          long userId = ((LongNode) node.get("user")).longValue();          //Based on the userId you need to search the user and build the user object properly         User user = new User(userId, ....);          return new Roles(id, roleName, user);     } } 

Then you need to register your new deserialiser (1) or use the @JsonDeserialize annotation (2)

(1)

ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(Item.class, new RolesDeserializer()); mapper.registerModule(module);  Roles deserializedRol = mapper.readValue(yourjson, Roles.class); 

(2)

@JsonDeserialize(using = RolesDeserializer.class) @Entity @Data public class Roles {     ... }  Roles deserializedRol = new ObjectMapper().readValue(yourjson, Roles.class); 

Answers 6

Jackson provide ObjectIdResolver interface for resolving the objects from ids during de-serialization.

In your case you want to resolve the id based from the JPA/hibernate. So you need to implement a custom resolver to resolve id by calling the JPA/hierbate entity manager.

At high level below are the steps:

  1. Implement a custom ObjectIdResolver say JPAEntityResolver (you may extends from SimpleObjectIdResolver). During resolving object it will call JPA entity manager class to find entity by given id and scope(see. ObjectIdResolver#resolveId java docs)

    //Example only; @Component @Scope("prototype") // must not be a singleton component as it has state public class JPAEntityResolver extends SimpleObjectIdResolver { //This would be JPA based object repository or you can EntityManager instance directly. private PersistentObjectRepository objectRepository;  @Autowired public JPAEntityResolver (PersistentObjectRepository objectRepository) {     this.objectRepository = objectRepository; }  @Override public void bindItem(IdKey id, Object pojo) {     super.bindItem(id, pojo); }  @Override public Object resolveId(IdKey id) {     Object resolved = super.resolveId(id);     if (resolved == null) {         resolved = _tryToLoadFromSource(id);         bindItem(id, resolved);     }      return resolved; }  private Object _tryToLoadFromSource(IdKey idKey) {     requireNonNull(idKey.scope, "global scope does not supported");      String id = (String) idKey.key;     Class<?> poType = idKey.scope;      return objectRepository.getById(id, poType); }  @Override public ObjectIdResolver newForDeserialization(Object context) {     return new JPAEntityResolver(objectRepository); }  @Override public boolean canUseFor(ObjectIdResolver resolverType) {     return resolverType.getClass() == JPAEntityResolver.class; } 

    }

  2. Tell Jackson to use a custom id resolver for a class, by using annotation JsonIdentityInfo(resolver = JPAEntityResolver.class). For e.g.

    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,               property = "id",               scope = User.class,               resolver = JPAObjectIdResolver.class) public class User { ... } 
  3. JPAObjectIdResolver is a custom implementation and will have dependency on other resources( JPA Entity Manager) which might not be known to Jackson. So Jackson need help to instantiate resolver object. For this purpose, you need to supply a custom HandlerInstantiator to ObjectMapper instance. (In my case I was using spring so I asked spring to create instance of JPAObjectIdResolver by using autowiring)

  4. Now de-serialization should work as expected.

Hope this helps.

Read More

Wednesday, February 8, 2017

Spring Boot: Wrapping JSON response in dynamic parent objects

Leave a Comment

I have a REST API specification that talks with back-end microservices, which return the following values:

On "collections" responses (e.g. GET /users) :

{     users: [         {             ... // single user object data         }     ],     links: [         {             ... // single HATEOAS link object         }     ] } 

On "single object" responses (e.g. GET /users/{userUuid}) :

{     user: {         ... // {userUuid} user object}     } } 

This approach was chosen so that single responses would be extensible (for example, maybe if GET /users/{userUuid} gets an additional query parameter down the line such at ?detailedView=true we would have additional request information).

Fundamentally, I think it is an OK approach for minimizing breaking changes between API updates. However, translating this model to code is proving very arduous.

Let's say that for single responses, I have the following API model object for a single user:

public class SingleUserResource {     private MicroserviceUserModel user;      public SingleUserResource(MicroserviceUserModel user) {         this.user = user;     }      public String getName() {         return user.getName();     }      // other getters for fields we wish to expose } 

The advantage of this method is that we can expose only the fields from the internally used models for which we have public getters, but not others. Then, for collections responses I would have the following wrapper class:

public class UsersResource extends ResourceSupport {      @JsonProperty("users")     public final List<SingleUserResource> users;      public UsersResource(List<MicroserviceUserModel> users) {         // add each user as a SingleUserResource     } } 

For single object responses, we would have the following:

public class UserResource {      @JsonProperty("user")     public final SingleUserResource user;      public UserResource(SingleUserResource user) {         this.user = user;     } } 

This yields JSON responses which are formatted as per the API specification at the top of this post. The upside of this approach is that we only expose those fields that we want to expose. The heavy downside is that I have a ton of wrapper classes flying around that perform no discernible logical task aside from being read by Jackson to yield a correctly formatted response.

My questions are the following:

  • How can I possibly generalize this approach? Ideally, I would like to have a single BaseSingularResponse class (and maybe a BaseCollectionsResponse extends ResourceSupport class) that all my models can extend, but seeing how Jackson seems to derive the JSON keys from the object definitions, I would have to user something like Javaassist to add fields to the base response classes at Runtime - a dirty hack that I would like to stay as far away from as humanly possible.

  • Is there an easier way to accomplish this? Unfortunately, I may have a variable number of top-level JSON objects in the response a year from now, so I cannot use something like Jackson's SerializationConfig.Feature.WRAP_ROOT_VALUE because that wraps everything into a single root-level object (as far as I am aware).

  • Is there perhaps something like @JsonProperty for class-level (as opposed to just method and field level)?

4 Answers

Answers 1

There are several possibilities.

You can use a java.util.Map:

List<UserResource> userResources = new ArrayList<>(); userResources.add(new UserResource("John")); userResources.add(new UserResource("Jane")); userResources.add(new UserResource("Martin")); Map<String, List<UserResource>> usersMap = new HashMap<String, List<UserResource>>(); usersMap.put("users", userResources); ObjectMapper mapper = new ObjectMapper(); System.out.println(mapper.writeValueAsString(usersMap)); 

You can use ObjectWriter to wrap the response that you can use like below:

ObjectMapper mapper = new ObjectMapper(); ObjectWriter writer = mapper.writer().withRootName(root); result = writer.writeValueAsString(object); 

Here is a proposition for generalizing this serialization.

A class to handle simple object:

public abstract class BaseSingularResponse {      private String root;      protected BaseSingularResponse(String rootName) {         this.root = rootName;     }      public String serialize() {         ObjectMapper mapper = new ObjectMapper();         ObjectWriter writer = mapper.writer().withRootName(root);         String result = null;         try {             result = writer.writeValueAsString(this);         } catch (JsonProcessingException e) {             result = e.getMessage();         }         return result;     } } 

A class to handle collection:

public abstract class BaseCollectionsResponse<T extends Collection<?>> {     private String root;     private T collection;      protected BaseCollectionsResponse(String rootName, T aCollection) {         this.root = rootName;         this.collection = aCollection;     }      public T getCollection() {         return collection;     }      public String serialize() {         ObjectMapper mapper = new ObjectMapper();         ObjectWriter writer = mapper.writer().withRootName(root);         String result = null;         try {             result = writer.writeValueAsString(collection);         } catch (JsonProcessingException e) {             result = e.getMessage();         }         return result;     } } 

And a sample application:

public class Main {      private static class UsersResource extends BaseCollectionsResponse<ArrayList<UserResource>> {         public UsersResource() {             super("users", new ArrayList<UserResource>());         }     }      private static class UserResource extends BaseSingularResponse {          private String name;         private String id = UUID.randomUUID().toString();          public UserResource(String userName) {             super("user");             this.name = userName;         }          public String getUserName() {             return this.name;         }          public String getUserId() {             return this.id;         }     }      public static void main(String[] args) throws JsonProcessingException {         UsersResource userCollection = new UsersResource();         UserResource user1 = new UserResource("John");         UserResource user2 = new UserResource("Jane");         UserResource user3 = new UserResource("Martin");          System.out.println(user1.serialize());          userCollection.getCollection().add(user1);         userCollection.getCollection().add(user2);         userCollection.getCollection().add(user3);          System.out.println(userCollection.serialize());     } } 

You can also use the Jackson annotation @JsonTypeInfo in a class level

@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=JsonTypeInfo.Id.NAME) 

Answers 2

Personally I don't mind the additional Dto classes, you only need to create them once, and there is little to no maintenance cost. And If you need to do MockMVC tests, you will most likely need the classes to deserialize your JSON responses to verify the results.

As you probably know the Spring framework handles the serialization/deserialization of objects in the HttpMessageConverter Layer, so that is the correct place to change how objects are serialized.

If you don't need to deserialize the responses, it is possible to create a generic wrapper, and a custom HttpMessageConverter (and place it before MappingJackson2HttpMessageConverter in the message converter list). Like this:

public class JSONWrapper {      public final String name;     public final Object object;      public JSONWrapper(String name, Object object) {         this.name = name;         this.object = object;     } }   public class JSONWrapperHttpMessageConverter extends MappingJackson2HttpMessageConverter {      @Override     protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {         // cast is safe because this is only called when supports return true.         JSONWrapper wrapper = (JSONWrapper) object;         Map<String, Object> map = new HashMap<>();         map.put(wrapper.name, wrapper.object);         super.writeInternal(map, type, outputMessage);     }      @Override     protected boolean supports(Class<?> clazz) {         return clazz.equals(JSONWrapper.class);     } } 

You then need to register the custom HttpMessageConverter in the spring configuration which extends WebMvcConfigurerAdapter by overriding configureMessageConverters(). Be aware that doing this disables the default auto detection of converters, so you will probably have to add the default yourself (check the Spring source code for WebMvcConfigurationSupport#addDefaultHttpMessageConverters() to see defaults. if you extend WebMvcConfigurationSupport instead WebMvcConfigurerAdapter you can call addDefaultHttpMessageConverters directly (Personally I prefere using WebMvcConfigurationSupport over WebMvcConfigurerAdapter if I need to customize anything, but there are some minor implications to doing this, which you can probably read about in other articles.

Answers 3

Jackson doesn't have a lot of support for dynamic/variable JSON structures, so any solution that accomplishes something like this is going to be pretty hacky as you mentioned. As far as I know and from what I've seen, the standard and most common method is using wrapper classes like you are currently. The wrapper classes do add up, but if you get creative with your inheretence you may be able to find some commonalities between classes and thus reduce the amount of wrapper classes. Otherwise you might be looking at writing a custom framework.

Answers 4

I guess you are looking for Custom Jackson Serializer. With simple code implementation same object can be serialized in different structures

some example: http://stackoverflow.com/a/10835504/814304 http://www.davismol.net/2015/05/18/jackson-create-and-register-a-custom-json-serializer-with-stdserializer-and-simplemodule-classes/

Read More

Thursday, February 2, 2017

Dynamically generating JSON schema with Jackson

Leave a Comment

Is there a Jackson equivalent to Spark SQL's StructField where I can build expected schema from scratch? I already know the name of the field and the type of data.

Jackson JsonSchema module looks like the closest solution, but the examples of how to construct the JSON schema seem to be lacking, and a solution that doesn't involve creating a new POJO (perhaps JsonNode?) is preferred. The problem I have been having with using JsonNode is that it requires a value, and the schema is not supposed to have any value.

0 Answers

Read More

Tuesday, January 24, 2017

Add dynamic fields to Spring JSON view response

Leave a Comment

There is a Spring approach to filter out fields from a service response with JSON views, but a am missing an equivalent approach to enrich a response with some dynamic/syntetic fields like this;

class User{     getFirstName(){...}     getLastName(){...}     getCreateDate(){...} }  class UserViewA{     getFullName(){       return getLastName()+", "+getFirstName()     }     getCreateDate(){...} }  class UserViewB{     getFullName(){       return getFirstName()+" "+getLastName()     }     getCreateDate(){...} } 

I could wrap the user within the view, but I do not want to propagate all needed user fields manually.

My other idea was to extend the views with the user object and create some sort of an reference linker to copy values references from the user object to the view, but this will get complicated with collections.

Is there some other approach or framework to achieve this? Is this concept not addressed in any way at all?

Update:

Clarification by example:

  • I do not want to wrap the User object because I do not want to maintain same getter methods from User class in different UserView objects.
  • I can not extend the User because its an domain object loaded from other resource.
  • There should be no reference to different UserView objects in the User object.

I am looking for a kind of facade solution/framework/approach.

4 Answers

Answers 1

How about using jackson @JsonUnwrapped?

http://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html

public class UserViewA {      @JsonUnwrapped     private User user;      public User getUser() ...      public String getFullName() {         return user.getFirstName() + " " + user.getLastName()     } } 

JsonUnwrapped will just pull all properties of User to the root level and still have the own properties of UserViewA in there.

Answers 2

When you cannot modify the domain object's class, you can enrich your JSON with "virtual" fields using a mix-in.

For example, you could create a class named UserMixin that hides the firstName and lastName fields and that exposes a virtual fullName field:

import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.annotation.JsonAppend;  import java.util.Date;  @JsonAppend(     prepend = true,     props = {             @JsonAppend.Prop(name = "fullName", value = UserFullName.class)     }) public abstract class UserMixin {     @JsonIgnore     public abstract String getFirstName();     @JsonIgnore     public abstract String getLastName();     public abstract Date getCreatedDate(); } 

Then you would implement a class named UserFullName that extends VirtualBeanPropertyWriter to provide the virtual field's value:

import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.introspect.AnnotatedClass; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter; import com.fasterxml.jackson.databind.util.Annotations;  public class UserFullName extends VirtualBeanPropertyWriter {     public UserFullName() {}      public UserFullName(BeanPropertyDefinition propDef, Annotations contextAnnotations, JavaType declaredType)     {         super(propDef, contextAnnotations, declaredType);     }      @Override     protected Object value(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception     {         return ((User) bean).getFirstName() + " " + ((User) bean).getLastName();     }      @Override     public VirtualBeanPropertyWriter withConfig(MapperConfig<?> config, AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type)     {         return new UserFullName(propDef, null, type);     } } 

Finally, you would need to register your mix-in with the ObjectMapper as shown in the following JUnit test:

@Test public void testUserFullName() throws IOException {     ObjectMapper objectMapper = new ObjectMapper();     objectMapper.addMixIn(User.class, UserMixin.class);     System.out.println(objectMapper.writeValueAsString(new User("Frodo", "Baggins"))); } 

The output is then:

{"fullName":"Frodo Baggins","createdDate":1485036953535} 

Answers 3

Actually, the best way to do that is wrap the User object (if your serialization framework uses getter method) or rewrite whole object to another view object (if your serialization framework uses property). If you don't want rewrite your object manually, you can use framework like dozer or other Java Bean mapping framework.

If your model look like this:

public class User {     private final String firstName;     private final String lastName;      // constructor and getter method } 

Your wrap class could look like this:

public class UserView {     private final User user;      public UserView(User user) {         this.user = user;     }      public String getFullName() {         return user.getFirstName() + " " + user.getLastName();     } } 

Or also you can rewrite whole object:

public class UserView {     private final String fullName;      public UserView(User user) {         this.fullName = user.getFirstName() + " " + user.getLastName();     }      // getter if needed } 

Answers 4

How'bout this?

@Transient @JsonProperty("fullName") getFullName(){   return getLastName()+", "+getFirstName() } 

Update based on your comment:

An approach I'd suggest is to create a UserView class which will use extension or composition (I think in this case extension would be ok). On each additional method (getFullName) you can apply specific view strategy manually based on your needs.

If you don't have sophisticated requirements, you can add all additional methods in the UserView (getFullNameA, getFullNameB) and annotate them with different JsonViews.

If you need to have one method (getFullName) instead of two with different name, you can have a getFullName which returns Arrays.asList(getFullNameA(), getFullNameB()).get(0)) and annotate the getFullNameA() and getFullNameB() with different Json View annotations. This way your list is going to have always one item, depending on the view used.

Read More

Wednesday, July 20, 2016

Using Jackson to map object from specific node in JSON tree

Leave a Comment

Is it possible to have Jackson's ObjectMapper unmarshall only from a specific node (and 'down') in a JSON tree?

The use case is an extensible document format. I want to walk the tree, and then publish the current path to an extensible set of plugins, to see if the user is using and plugins that know what to do with that part of the document.

I'd like for plugin authors to not have to deal with the low-level details of JsonNode or the streaming API; instead, just be passed some context and a specific JsonNode, and then be able to use the lovely and convenient ObjectMapper to unmarshall an instance of their class, considering the node passed as the root of the tree.

1 Answers

Answers 1

Consider the following JSON:

{   "firstName": "John",   "lastName": "Doe",   "address": {     "street": "21 2nd Street",     "city": "New York",     "postalCode": "10021-3100",     "coordinates": {       "latitude": 40.7250387,       "longitude": -73.9932568     }   } } 

And consider you want to parse the coordinates node into the following Java class:

public class Coordinates {      private Double latitude;      private Double longitude;      // Default constructor, getters and setters omitted } 

To do it, parse the whole JSON into a JsonNode with ObjectMapper:

ObjectMapper mapper = new ObjectMapper(); JsonNode node = mapper.readTree("{\"firstName\":\"John\",\"lastName\":\"Doe\",\"address\":{\"street\":\"21 2nd Street\",\"city\":\"New York\",\"postalCode\":\"10021-3100\",\"coordinates\":{\"latitude\":40.7250387,\"longitude\":-73.9932568}}}"); 

Then use JSON Pointer to query the coordinates node and use ObjectMapper to parse it into the Coordinates class:

JsonNode coordinatesNode = node.at("/address/coordinates"); Coordinates coordinates = mapper.treeToValue(coordinatesNode, Coordinates.class); 

JSON Pointer is path language to traverse JSON. For more details, check the RFC 6901. It is available in Jackson since the 2.3 version.

Read More

Saturday, May 7, 2016

Strange Jackson Illegal character ((CTRL-CHAR, code 0)) Exception in Map Reduce Combiner

Leave a Comment

I have a Map-Reduce job with a mapper which takes a record and converts it into an object, an instance of MyObject, which is marshalled to JSON using Jackson. The value is just another Text field in the record.

The relevant piece of the mapper is something like the following:

ObjectMapper mapper = new ObjectMapper(); MyObject val = new MyObject(); val.setA(stringA); val.setB(stringB); Writer strWriter = new StringWriter(); mapper.writeValue(strWriter, val); key.set(strWriter.toString()); 

The outputs of the mapper are sent to a Combiner which unmarshalls the JSON object and aggregates key-value pairs. It is conceptually very simple and is something like:

public void reduce(Text key, Iterable<IntWritable> values, Context cxt)      throws IOException, InterruptedException {     int count = 0;     TermIndex x = _mapper.readValue(key.toString(), MyObject.class);     for (IntWritable int : values) ++count;     ...     emit (key, value) } 

The MyObject class consists of two fields (both strings), get/set methods and a default constructor. One of the fields stores snippets of text based on a web crawl, but is always a string.

public class MyObject {   private String A;   private String B;    public MyObject() {}    public String getA() {     return A;   }   public void setA(String A) {     this.A = A;   }   public String getB() {     return B;   }    public void setIdx(String B) {     this.B = B;   } } 

My MapReduce job appears to be running fine until it reaches certain records, which I cannot easily access (because the mapper is generating the records from a crawl), and the following exception is being thrown:

Error: com.fasterxml.jackson.core.JsonParseException:       Illegal character ((CTRL-CHAR, code 0)): only regular white space (\r, \n, \t) is allowed between tokens      at [Source: java.io.StringReader@5ae2bee7; line: 1, column: 3] 

Would anyone have any suggestions about the cause of this?

1 Answers

Answers 1

Use a JSON library like Gson (add dependencies to Hadoop classpath) to escape invalid characters in your string.

Read More