Infinite recursion when deserializing with Jackson and Mockito - java

I am trying to generically print any object with mapper.writeValueAsString but I am facing a infinite recursion when deserializing objects with Mockito and Jackson. The objects I am trying to deserialize perform underlying Hibernate calls to DBs etc and I have no control over the classes themselves.
Currently I am using the following versions of Mockito and Jackson but the same is happening for older 1.X versions of Jackson.
Mockito: org.mockito:mockito-all:jar:1.9.5:compile
Jackson: com.fasterxml.jackson.core:jackson-databind:jar:2.9.7:compile
As specified, I cannot modify the underlying classes with annotation such as #JsonIgnore because they are outside dependencies not under my control. I also cannot create mixins for my user case because I am trying to generically print the contents of any object that is sent in.
I have tried adding the DeserializationConfig FAIL_ON_UNKNOWN_PROPERTIES to false in older Jackson version and I have tried setting the DeserializationFeature FAIL_ON_MISSING_CREATOR_PROPERTIES to false.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public static PrintUtil {
public static String printJSON(Object obj) {
String printstring = "printfailed";
try {
ObjectMapper mapper = new ObjectMapper();
LOG.debug("formatted JSON String ");
printstring = mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return printstring;
}
}
The infinite recursion terminal output is seen when running Mockito tests for methods that contain Log4j statements which in turn call the PrintUtil function. The statement e.printStackTrace() begins printing while running the tests.
Most of the object that are being sent to this utility method are JAXB XML Service Response Objects.

Without being able to modify the classes themselves, I see two possible solutions.
1) Wrap the objects into objects you own as suggested by #TheHeadRush and annotate it appropriately. I would suggest using #JsonIdentityInfo so the objects serialize to their ID, rather than being ignored completely with #JsonIgnore.
2) Use a custom deserializer for the object which is causing the recursion. Here is an example:
public class CustomDeserializer extends StdDeserializer<MyObject>{
// Add constructors as necessary.
#Override
public List<Item> deserialize(
JsonParser jsonparser,
DeserializationContext context)
throws IOException, JsonProcessingException {
return null; // Return something that won't recurse here.
}
}
Then register the deserializer with the ObjectMapper you are using:
// Register the deserializer:
SimpleModule module = new SimpleModule()
.addDeserializer(MyObject.class, new CustomDeserializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);

Related

Use JsonIgnore for an attribute in an other class

I meet an issue with a class contained in a library that I use.
This issue comes when I want deserialize it.
Indeed, this class has a method names "getCopy" which returns a new instance of himself which contains this same method and call it still a StackOverFlowException on the following cycle :
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:166)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:723)
public class Object {
...
ObjectAttribute objectAttribute;
...
public ObjectAttribute getObjectAttribute(){
return this.objectAttribute
}
...
}
public class ObjectAttribute{
...
public ObjectAttribute getCopy{
return copy(this) //return a new instance of himself
}
...
}
Is there a way to ignore the method getCopy() like #JsonIgnoreAttribute("objectProperty.copy")?
For this specific use case, when you have a class in a third party library that you are not able to modify, Jackson provides the Mix-in annotations.
The idea behind this concept is to provide a class that indicates how the serialization of another class should be accomplished.
For instance, consider the following mix-in class definition for your use case:
public abstract class ObjectAttributeMixIn{
// You need to provide definitions for every property you need
// to serialize, and the proper constructor if necessary
...
// Ignore the getCopy method
#JsonIgnore
public abstract ObjectAttribute getCopy();
...
}
You can use the full set of Jackson annotations in the mix-in definitions.
Then, associate the mix-in with the ObjectAttribute class. You can use the instance of ObjectMapper you are using for serialization for this purpose:
objectMapper.addMixInAnnotations(ObjectAttribute.class, ObjectAttributeMixIn.class);
Yon can also register a custom module instead; please, see the relevant documentation.
for ignore method getCopy, just enough rename this method , e.g copy
every method start with get then serialized ,e.g if method name is getSomething then serialized to something: (return value by method))
so if you change method name to copy or copyInstance or every name without start by get then method not serialized
You can override JsonSerializer and do specific logic for class
public class CustomSerializerForC extends JsonSerializer<C> {
#Override
public Class<C> handledType() {
return C.class;
}
#Override
public void serialize(C c, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
String upperCase = c.getValue().toUpperCase();
jsonGenerator.writeString(upperCase);
}
}
And use Serializer in moudle used in ObjectMapper:
SimpleModule module = new SimpleModule("MyCustomModule", new Version(1, 0, 0, null));
module.addSerializer(new CustomSerializerForC());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
There are 2 ways I see how to figure out your issue:
Write custom deserializer for you specific class and register it in Jackson mapper.
Tune up global Jackson mapper to ignore class getters in auto-detection and use only fields.
Please try 2 way with following config:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(JsonMethod.ALL, Visibility.NONE);
mapper.setVisibility(JsonMethod.FIELD, Visibility.ANY);
If you decide to move forward with 1 way, please write here if you need help.
You can register serializer and choose the fields you would like
/**
* We can not change source code so we are adding serializer for a specific type.
*
*/
public static class JsonSpecificTypeSerializer extends JsonSerializer<SpecificType> {
#Override
public void serialize(SpecificType t, JsonGenerator jsonGen, SerializerProvider serializerProvider) throws IOException {
jsonGen.writeStartObject();
jsonGen.writeFieldName("field1");
jsonGen.writeNumber(t.getield1());
.......
jsonGen.writeEndObject();
}
}
/**
* Customize jackson.
*
* adding configuration to jackson without overriding spring boot default conf.
*/
#Bean
public Jackson2ObjectMapperBuilderCustomizer customizeJackson() {
return jacksonObjectMapperBuilder -> {
jacksonObjectMapperBuilder.serializerByType(SpecificType.class,
new JsonSpecificTypeSerializer());
};
}

Junit test - Eclemma Coverage in Jackson toString

I want to have 100% coverage on the method toString overriden with Jackson JSON.
#Override
public String toString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
logger.error(e.getMessage());
return "";
}
}
I can make a test that can coverage the most of the code except the catch block.
#Test
public void testToString() {
TestClass testClass = new TestClass();
String expected = "{\"testAttr\":null}";
assertEquals(expected, testClass.toString());
}
How could I make a test that covers the catch block?
Of course you cold try to trigger that exception somehow with setting the enclosing object to some weird kind of state. But a better way to achieve full code coverage is mocking the mapper and making it throw the desired exception. Generally the steps that you need are:
Transfer the creation of ObjectMapper to a new method 'getObjectMapper'. During runtime it will decide if it returns a real or a fake mapper.
In your test inject a fake ObjectMapper and make 'getObjectMapper' return the fake mapper instead of a new mapper.
When the writeValueAsString method is called make it throw a JsonProcessingException
Run your test
In theory you could manually create the fake ObjectMapper e.g. by creating a subclass of it but that's not the recommended way. I would recommend using a mocking framework. A mocking framework lets you express things like "when method A is called then throw exception B".
You can define some attributes to your class and assert the return of the string.
You've already tested the null object.
Raise the exception and see if it handles it.
Like:
when(mapper.writeValueAsString(any(Object.class))).thenThrow(new JsonProcessingException("Error"){});
And also helpful link

Correct design to construct an object

I have a major design concern about my specific use-case in object construction.
I am constructing an object MyMessage based on the value of the com.fasterxml.jackson.databind.JsonNode object. For that purpose, of course, the ObjectMapper is used, so the actual construction snippet would look something like following:
JsonNode json = JsonLoader.fromResource("msg.json");
MyMessage m = jsonMapper.treeToValue(json, MyMessage.class);
Here the jsonMapper variable is of the type ObjectMapper.
Now, I would like MyMessage to only be able to construct from a static factory method, and keep the actual constructor private (as it has way too many fields). So the preferred construction would like this:
MyMessage m = MyMessage.createFromJson(json);
Here a json variable is of type JsonNode.
And that is where the problem begins. Obviously, ObjectMapper instance is needed to construct the object from JSON, and I would try to avoid it being passed-in by the client code.
Now, making ObjectMapper as a private (static) field of MyMessage comes to mind, and have something like this:
private static final ObjectMapper jsonMapper = new ObjectMapper();
private MyMessage(/* many fields */){/* ctor code */}
public MyMessage createFromJson(JsonNode json) {
return jsonMapper.treeToValue(json, MyMessage.class)
}
But here the problem is that I want the ObjectMapper object to be singleton for my entire application, and used not only for constructing MyMessage object, but also different kind of Message objects such as YourMessage, SomeOtherMessage, etc.
The problem with having many (static) instances of ObjectMapper is that I will have very large number of different Message classes, and many instances per class. So this would be very memory inefficient.
The Question
What is the closest I can get to have both a singleton ObjectMapper and a static factory method? Or maybe you could suggest some other design idea (I don't know if a builder pattern could help somehow) that would suit my use-case?
I'd suggest you move away from the static factory method restriction, and have a Factory that creates your messages, and make that class have a reference to your singleton ObjectMapper:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
final class MessagesFactory {
private final ObjectMapper objectMapper;
// This constructor is a great candidate for DI
MessagesFactory(final ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
<T extends Message> T createFromJson(
final JsonNode json, final Class<T> messageClass
) throws JsonProcessingException {
return objectMapper.treeToValue(json, clazz);
}
}
Then, wherever you need it, you can do:
MyMessage m1 = messagesFactory.createFromJson(json, MyMessage.class);
MyMessage2 m2 = messagesFactory.createFromJson(otherJson, MyMessage2.class);

Intercept Deserialization in Jackson

I want to hook into Jackson's deserialization to optionally deserialize a different JSON document than the one provided. That seems like a really weird use case so let me explain.
I am using the Amazon SQS Extended client to put messages that are too large for SQS on S3 instead and a message that looks like this through SQS
["com.amazon.sqs.javamessaging.MessageS3Pointer",{"s3BucketName":"my-bucket","s3Key":"f5a0fa29-7f9c-4852-8bbb-53697799efe2"}]
An elastic beanstalk worker is listening to the other end of that which means that those messages are POSTed to a Jersey endpoint my application maintains. Since those messages are POSTed instead of using a SQS receiveMessage call the extended client will not fetch the message from S3 itself.
I was thinking it would be pretty clever to make a custom JsonDeserializer that would look at the message to see if it was an S3 pointer, download that file, and deserialize it. Otherwise, just deserialize the provided message. However, that isn't working out quite as smoothly as I hoped.
Here is what I have so far:
public class SQSS3Deserializer<T> extends JsonDeserializer<T> {
private static final String s3PointerHeader = "com.amazon.sqs.javamessaging.MessageS3Pointer";
private Class<T> type;
private ObjectMapper mapper = new ObjectMapper();
public SQSS3Deserializer() {
super();
type = getParameterizedTypeArgument();
}
#Override
public T deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
if (jp.isExpectedStartArrayToken()) {
jp.nextToken();
if (s3PointerHeader.equals(jp.getValueAsString())) {
jp.nextToken();
S3Pointer p = jp.readValueAs(S3Pointer.class);
return mapper.readValue(S3Utils.getInputStream(p.s3BucketName, p.s3Key), type);
}
}
return jp.readValueAs(type);
}
#SuppressWarnings("unchecked")
protected Class<T> getParameterizedTypeArgument() {
return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
static private class S3Pointer {
public String s3BucketName;
public String s3Key;
}
}
For each POJO I want to deserialize I'll have to create an empty subclass with the correct generic specialization, for example:
public class POJOS3Deserializer extends SQSS3Deserializer<POJO> {}
I also will need to add the JsonDeserializer annotation to the class
#JsonDeserialize(using=POJOS3Deserializer.class)
public class POJO { ... }
However, doing it this way causes a stack overflow error because it will continually reenter my deserializer when it calls JsonParser.readValueAs() since readValueAs looks at the JsonDeserialize annotation.
So, I have two questions:
How do I change this to keep this fairly generic and still have Jackson do most of the heavy lifting of parsing while avoiding that recursive call?
Is there a way to remove the need to derive from SQSS3Deserializer for each POJO I want to deserialize this way?
Thanks

IllegalStateException occurs when using Jackson #JsonDeserialize annotation

I'm trying to use a custom deserializer in Jackson to deserialize some json objects. However, when I try to have the ObjectMapper read the json, the following exception occurs:
java.lang.IllegalStateException: AnnotationIntrospector returned Class com.Geometry.GeometryDeserializer; expected Class<JsonDeserializer>
I'm somewhat at a loss of what to do here, since it seems like the AnnotationIntrospector is complaining that my GeometryDeserializer is not a subclass of JsonDeserializer, when it clearly is.
Here's where I create the Object Mapper:
public void deserializeJson(String json) {
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Feature.class, MixIn.class);
Feature feature = mapper.readValue(json, Feature.class);
}
...my Mix In class:
abstract class MixIn {
#JsonDeserialize(using=GeometryDeserializer.class)
abstract void setGeometry(Geometry geometry);
}
...and my deserializer:
public class GeometryDeserializer extends JsonDeserializer<Geometry> {
#Override
public Geometry deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
//stuff happens
}
}
Any feedback/assistance would be greatly appriciated.
Thanks.
Wild guess: you are accidentally mixing up Jackson 1.x and Jackson 2.x types? Class names are mostly the same, but live in different packages -- things work when using one set of classes, but IDEs may cause accidental mix-ups.

Categories

Resources