JsonMappingException not Serializable due to 2.7 reference to underlying parser
See original GitHub issueJsonMappingExceptions thrown by the Jackson parser cannot be serialized. This breaks any code that assumes the exception (per the Serializable interface it implements) is serializable.
Since the exception contains a reference to a non-serializable parser (such as com.fasterxml.jackson.core.json.ReaderBasedJsonParser), serialization will throw a NotSerializableException. Furthermore, the parser may contain a reference to the class being parsed; if that class is not itself serializable, that too will prevent serialization.
The expected behavior would be that these exceptions would be serializable.
I ran across this issue when testing some code with a missing subtype. The following code will replicate this issue: import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.ByteArrayOutputStream;
import java.io.NotSerializableException;
import java.io.ObjectOutputStream;
public class SerializationTest {
public static void main(String[] args) throws Exception {
try {
new ObjectMapper().readValue("{\"type\": \"B\"}", ClassToRead.class);
} catch (JsonMappingException e) {
try (ObjectOutputStream stream = new ObjectOutputStream(new ByteArrayOutputStream())) {
stream.writeObject(e);
} catch (NotSerializableException e2) {
// java.io.NotSerializableException: com.fasterxml.jackson.core.json.ReaderBasedJsonParser
System.err.println("Unable to serialize exception " + e);
e2.printStackTrace();
}
}
try {
new ObjectMapper().readValue("{\"classToRead\": {\"type\": \"B\"}}", ContainerClassToRead.class);
} catch (JsonMappingException e) {
try (ObjectOutputStream stream = new ObjectOutputStream(new ByteArrayOutputStream())) {
stream.writeObject(e);
} catch (NotSerializableException e2) {
// java.io.NotSerializableException: jackson.ContainerClassToRead
System.err.println("Unable to serialize exception " + e);
e2.printStackTrace();
}
}
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
@JsonSubTypes.Type(value = SubclassToRead.class, name = "A")
})
abstract class ClassToRead {}
class SubclassToRead extends ClassToRead {}
class ContainerClassToRead {
public ClassToRead classToRead;
}
Issue Analytics
- State:
- Created 7 years ago
- Comments:9 (6 by maintainers)
Top GitHub Comments
Yes final fields must be set from within constructor, but what I meant was that I thought whoever constructed instances was not always in position to pass in all the information; and that this was why there were setters (
setIndex()
etc). I use final fields and immutable objects wherever possible otherwise, so I must have had some reason not to do that here. Another possible reason may have been that instances would be Jackson serializable/deserializable, not just JDK serializable (which in a way is secondary concern).But looking at call chains it does not actually look like anything was actually calling setters so there does not seem current need to do that. I’ll clean this up for 2.8; for 2.7 I will leave things mostly as are just to keep even low likelihood problems at bay.
Thanks for the explanation; I haven’t had to work much with implementing serialization behavior, so I was wondering if that was intentional or not.
Six of one, half dozen of the other, I suspect; but it occurs to me that if you made the _from field transient, you could just initialize the _asString property and let the serialization do its thing with the same object. I don’t think it buys you anything, though:
As to the mutability thing, as long as the final fields are not initialized inline, you should be able to set them in the constructor. In fact, it wouldn’t compile unless the constructor was setting all the final fields.