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?