Lazy Diary @ Hatena Blog

PowerShell / Java / miscellaneous things about software development, Tips & Gochas. CC BY-SA 4.0/Apache License 2.0

Get a deep copy of an object with non-serializable fields with Jackson

Background

When you want to get a deep copy of an object, you can use ObjectOutputStream/ObjectInputStream and serialize/deserialize the target object *1 ((Note that SerializationUtils.clone() in Spring Framework requires the class of the target object implements Serializable. ObjectOutputStream#writeObject() doesn't have such restriction)).

Problem

In this method, you will get NotSerializableException if the object contains non-serializable fields like MultipartFile of Spring Framework.

To avoid this exception, you have to add the transient modifier to the non-serializable fields. So you have to have control of the class of the object to adopt this workaround. For the class that you don't have control of or that is in libraries, you can't take this approach.

Solution

You can use Jackson's ObjectMapper#writeValueAsString() and ObjectMapper#readValue() to clone an object. This method throws InvalidDefinitionException if the object has non-serializable fields. You can use @JsonIgnoreType and ObjectMapper#addMixIn() to ignore non-serializable fields according to their type without changing the definition of the class.

For example, you can ignore the MultipartFile fields (for single file upload) and MultipartFile[] fields (for multiple file upload) like this:

private Object makeClone(Object obj) {
    ObjectMapper mapper = new ObjectMapper();
    mapper.addMixIn(MultipartFile.class, JacksonMixInForIgnoreType.class);
    mapper.addMixIn(MultipartFile[].class, JacksonMixInForIgnoreType.class);
    try {
        return mapper.readValue(mapper.writeValueAsString(obj), obj.getClass());
    } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }
}

@JsonIgnoreType
class JacksonMixInForIgnoreType {}

Restriction

  • You can't ignore the field like List<MultipartFile> fileArray; with this strategy. It seems there is no way to ignore the collection of a specific class.
  • If the target object has two or more references to a single instance, they will be different objects on the clone.