Get parent json string inside DeserializationProblemHandler (Jackson)

Viewed 94

I'm working with a remote API which can change and I have no control over it. This is why I have written myself a small jackson module that has a DeserializationProblemHandler implementation which logs those "unknown properties". This works just fine, but I want to make my life easier and log the actual received JSON in the logs as well.

So having the following JSON example:

{ "myUnknownProperty": "value123", "myIgnoredProperty": true, "myKnownProperty": true }

A log should look something similar to this:

WARN: Found unexpected property while parsing json: /myUnknownProperty (type: com.foo.PartiallyIgnoredPOJO); JSON { "myUnknownProperty": "value123", "myIgnoredProperty": true, "myKnownProperty": true }

Here is the test case I currently have:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.io.IOException;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class ExampleTest
{

    // Test
    @Test
    public void mockTest() throws JsonProcessingException
    {
        Logger logger = mock(Logger.class);
        try
        {
            new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                              .registerModule(new UnknownPropertyModule(logger))
                              .readValue(partiallyIgnoredSONSample, PartiallyIgnoredPOJO.class);
        }
        finally
        {
            verify(logger).warn(UnknownPropertyModule.MESSAGE, 
                                "/myUnknownProperty", 
                                PartiallyIgnoredPOJO.class,
                                partiallyIgnoredSONSample);
        }
    }

    // Implementations
    private static class UnknownPropertyDeserializationProblemHandler extends DeserializationProblemHandler
    {

        private final Logger logger;
        private final String format;

        UnknownPropertyDeserializationProblemHandler(final Logger logger, final String format)
        {
            this.logger = logger;
            this.format = format;
        }

        @Override
        public boolean handleUnknownProperty(final DeserializationContext context,
                                             final JsonParser parser,
                                             final JsonDeserializer<?> deserializer,
                                             final Object beanOrClass,
                                             final String propertyName) throws IOException
        {
            final Class<?> type = beanOrClass.getClass();
            String jsonPath = parser.getParsingContext().pathAsPointer().toString();
            String json = "__How_to_get_the_entire_JSON_from_the_root___";
            logger.warn(format, jsonPath, type, json);
            return false;
        }

    }

    private static class UnknownPropertyModule extends Module
    {
        public static final String MESSAGE = "Found unexpected property while parsing json: {} (type: {}); JSON: {}";
        private final Logger logger;

        public UnknownPropertyModule(@Nullable final Logger logger)
        {
            this.logger = logger != null ? logger : LoggerFactory.getLogger(getClass().getName());
        }

        @Override
        public String getModuleName()
        {
            return getClass().getSimpleName();
        }

        @Override
        public Version version()
        {
            return Version.unknownVersion();
        }

        @Override
        public void setupModule(Module.SetupContext context)
        {
            context.addDeserializationProblemHandler(new UnknownPropertyDeserializationProblemHandler(logger, MESSAGE));
        }
    }

    // Example json & mapping.
    private static final String partiallyIgnoredSONSample = "{ \"myUnknownProperty\": \"value123\", \"myIgnoredProperty\": true, \"myKnownProperty\": true }";

    @JsonIgnoreProperties("myIgnoredProperty")
    private static class PartiallyIgnoredPOJO
    {

        private final boolean myKnownProperty;

        @JsonCreator
        public PartiallyIgnoredPOJO(@JsonProperty("myKnownProperty") boolean myKnownProperty)
        {
            this.myKnownProperty = myKnownProperty;
        }

        public boolean getMyKnownProperty()
        {
            return myKnownProperty;
        }

    }

}

From what I've seen in the debugger, you could use parser.getParsingContext().getParent() to get to the json root, but I'm unsure how to get the actual buffered data. Any ideas?

0 Answers
Related