Cannot use property annotations on StackTraceElement to configure deserialization (original) (raw)

I am working on Apache Log4j 2.0 (in beta) with Jackson 2.3.2 and I must override the stock deserializer to read JSON StackTraceElements that look like this:

{"class":"package.SomeClass","method":"someMethod","file":"SomeClass.java","line":123}

Simple enough I thought, mix-ins are perfect for this, a non-intrusive design pattern:

@JsonIgnoreProperties("nativeMethod") abstract class StackTraceElementMixIn { @JsonCreator StackTraceElementMixIn(@JsonProperty("class") String declaringClass, @JsonProperty("method") String methodName, @JsonProperty("file") String fileName, @JsonProperty("line") int lineNumber) { // empty }

@JsonProperty("class")
abstract String getClassName();

@JsonProperty("file")
abstract String getFileName();

@JsonProperty("line")
abstract String getLineNumber();

@JsonProperty("method")
abstract String getMethodName();

}

and:

public class Log4jObjectMapper extends ObjectMapper {

private static final long serialVersionUID = 1L;

public Log4jObjectMapper() {
    registerModule(new Log4jModule());
}

}

and:

public class Log4jModule extends SimpleModule {

public Log4jModule() {
    super(Log4jModule.class.getName(), new Version(2, 0, 0, null, null, null));
}

@Override
public void setupModule(SetupContext context) {
    context.setMixInAnnotations(StackTraceElement.class, StackTraceElementMixIn.class);
}

}

It does not work. Jackson uses its own deserializer com.fasterxml.jackson.databind.deser.std.StackTraceElementDeserializer.

But... serialization works fine! As a test, I used the mix-in above to produce the same JSON text I listed above.

Surely, serialization and deserialization should behave the same with regard to mix-ins?

As a workaround, I made a copy of StackTraceElementDeserializer and changed the Java package and class name and JSON attribute names in the code, then I did this in my domain class:

@JsonProperty("Location")
@JsonDeserialize(using=MyStackTraceElementDeserializer.class)
private StackTraceElement location;

and it still does not work! The UnrecognizedPropertyException is the same:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "class" (class java.lang.StackTraceElement), not marked as ignorable
 at [Source: java.io.StringReader@32b427c1; line: 1, column: 11] (through reference chain: java.lang.StackTraceElement["class"])
    at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
    at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:671)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:771)
    at com.fasterxml.jackson.databind.deser.std.StackTraceElementDeserializer.deserialize(StackTraceElementDeserializer.java:47)
    at com.fasterxml.jackson.databind.deser.std.StackTraceElementDeserializer.deserialize(StackTraceElementDeserializer.java:11)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2993)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2098)
    at org.apache.logging.log4j.core.jackson.StackTraceElementMixInTest.testObjectMapper(StackTraceElementMixInTest.java:31)

Test:

import org.junit.Assert; import org.junit.Test;

import com.fasterxml.jackson.databind.ObjectMapper;

public class StackTraceElementMixInTest {

@Test
public void testObjectMapper() throws Exception {
    ObjectMapper objectMapper = new Log4jObjectMapper();
    StackTraceElement expected = new StackTraceElement("package.SomeClass", "someMethod", "SomeClass.java", 123);
    final String s = objectMapper.writeValueAsString(expected);
    StackTraceElement actual = objectMapper.readValue(s, StackTraceElement.class);
    Assert.assertEquals(expected, actual);
}

Help!

Thank you,

Gary