Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I retain the type property when deserializing with Jackson's @JsonTypeInfo?

I have a setup like this:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "dishName", defaultImpl = Food.class)
@JsonSubTypes(value = {
    @Type(name = "fries", value = Fries.class),
    @Type(name = "burger", value = Burger.class)
})
public class Food {
  private String dishName;

  @Override
  public String toString() {
    return dishName + ", type: " + this.getClass().getName();
  }
}

public class Fries extends Food { /*...*/ }

public class Burger extends Food { /*...*/ }

public class TryItOut {

  private static String foodString = "[ { \"dishName\":\"burger\" }, { \"dishName\":\"fries\" }, { \"dishName\":\"cabbage\" } ]";

  public static void main(String[] args) {
    ObjectMapper m = new ObjectMapper();
    try {
        Food[] food = m.readValue(foodString, Food[].class);
        for (Food dish : food) {
            System.out.println(dish);
        }
    } catch (IOException e) {
        System.out.println("something went wrong");
        e.printStackTrace();
    }
  }
}

I want to use this to deserialize json whiches content I cannot influence (so no option to add "proper" type information). The problem I have is that apparently the dishName json property is used to determine the subtype all right, but it is not deserialized into the java field. Is there a way to achieve that, too? In other words: the main method prints

null, type: Burger
null, type: Fries
null, type: Food

on the console, but I want it to print

burger, type: Burger
fries, type: Fries
cabbage, type: Food

this is particularly nasty because there is no way for me to find out later on that the last object is cabbage. That nullifies the benefits of the default implementation.

EDIT:

@Evil Raat 's answer does the trick. For the sake of completeness: the dishName field in the Food class needs the @JsonProperty Annotation for this example to work. The working example thus looks like this:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "dishName", defaultImpl = Food.class, visible = true)
@JsonSubTypes(value = {
    @Type(name = "fries", value = Fries.class),
    @Type(name = "burger", value = Burger.class)
})
public class Food {

  @JsonProperty
  private String dishName;

  @Override
  public String toString() {
    return dishName + ", type: " + this.getClass().getName();
  }
}

public class Fries extends Food { /*...*/ }

public class Burger extends Food { /*...*/ }

public class TryItOut {

  private static String foodString = "[ { \"dishName\":\"burger\" }, { \"dishName\":\"fries\" }, { \"dishName\":\"cabbage\" } ]";

  public static void main(String[] args) {
    ObjectMapper m = new ObjectMapper();
    try {
        Food[] food = m.readValue(foodString, Food[].class);
        for (Food dish : food) {
            System.out.println(dish);
        }
    } catch (IOException e) {
        System.out.println("something went wrong");
        e.printStackTrace();
    }
  }
}
like image 470
Ole Avatar asked Mar 06 '15 09:03

Ole


1 Answers

To maintain the value of the property you used for the Deserialization, you just have to set the visible property to true on the @JsonSubTypes annotation:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "dishName", defaultImpl = Food.class, visible = true)

like image 142
pmckeown Avatar answered Oct 19 '22 06:10

pmckeown