Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson serialise map with extra fields

Tags:

java

json

jackson

If a class extends a Map and includes some extra fields. E.G a last modified date, Jackson seems to ignore any thing not contained as a property of the map.

I have a class that looks some thing like the following:

import java.io.IOException;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Objects;

import com.fasterxml.jackson.databind.ObjectMapper;

public class Foo extends HashMap<String, String> {

   private OffsetDateTime lastModifiedTime;

   public Foo() {
      super();
      lastModifiedTime = OffsetDateTime.now();
   }

   public void setLastModifiedTime(OffsetDateTime newTime) {
      this.lastModifiedTime = newTime;
   }

   public OffsetDateTime getLastModifiedTime() {
      return this.lastModifiedTime;
   }

   public static void main(String[] args) throws IOException {
      Foo f = new Foo();
      f.put("hello", "world");

      ObjectMapper om = new ObjectMapper();
      om.findAndRegisterModules();

      String result = om.writeValueAsString(f);
      if(f.equals(om.readValue(result, Foo.class))) {
         System.out.println("Wooo");
      } else {
         System.out.println("Booo: " + result);
      }

   }

   @Override
   public boolean equals(Object obj) {
      if (this == obj) {
         return true;
      }
      if (!(obj instanceof Foo)) {
         return false;
      }
      if (!super.equals(obj)) {
         return false;
      }
      Foo foo = (Foo) obj;
      return Objects.equals(getLastModifiedTime(), foo.getLastModifiedTime());
   }

   @Override
   public int hashCode() {
      return Objects.hash(super.hashCode(), getLastModifiedTime());
   }

}

When this runs it outputs booo: {"hello":"world"} Which does not include the last modified date.

I've tried adding @JsonInclude annotations to the property and the getters but that doesn't cause it to include the extra field.

What is the correct way to make Jackson include this extra field?

like image 451
Wil Selwood Avatar asked Jul 09 '15 14:07

Wil Selwood


2 Answers

It's generally considered bad practice to extend HashMap. Instead, you should create a class that has a Map, and the additional field.

This will solve your problem, but will also have additional advantages:

  • you will be able to encapsulate the rules (like preventing some keys/values to be added, etc.)
  • you will be able to change the Map implementation if needed (LinkedHashMap, synchronized map, concurrent map)
like image 136
JB Nizet Avatar answered Oct 27 '22 15:10

JB Nizet


By default your map will fall into MapSerializer. You could register a serializer modifier to add the custom property, but you would be better off nesting the map as a property of Foo and unwrapping it:

@JsonUnwrapped
private HashMap<String, String> fooMap;
like image 45
Sam Berry Avatar answered Oct 27 '22 16:10

Sam Berry