Search code examples
javajacksonguavainstanceofpredicates

Why does Predicates.instanceOf returns false?


I have a

String s =
    {
      "code1" : {
        "price" : 100,
        "type" : null
      },
      "code2" : {
        "price" : 110,
        "type" : null
      }
    }

Then I do:

Object p = Mapper.readValue(s, Person.class);

So it executes the method annotated with @JsonCreator in Person.class:

@JsonCreator
static Person create(Map<String, Object> s) {
        s = Maps.filterValues(s, Predicates.instanceOf(Person.class));
        ...
    }

My problem is s is always empty. I checked and the values have a price and a type. But when I do ps.get("code1").getClass(), it gives me LinkedHashMap.

I don't understand what is happening... Do you have any clue?

This is my class Person (it's an inner class):

public static class Person{

        private int price;
        private String type;
        public Person(int price) {
            this.price = price;
        }
        public int getPrice() {
            return price;
        }
        public String getType() {
            return type;
        }
    }

Thanks!


Solution

  • The problem is that you are deserializing json String to Object and you will always have LinkedHashMap there, because java.lang.Object doesn't have any custom field.

    Just try a different way:

      public  class Demo {
            public static void main(String[] args) throws IOException {
                String s = "{" +
                        "  \"code1\" : {" +
                        "    \"price\" : 100," +
                        "    \"type\" : null" +
                        "  }," +
                        "  \"code3\" : {" +
                        "    \"somethingElsse\" : false," +
                        "    \"otherType\" : 1" +
                        "  }," +
                        "  \"code2\" : {" +
                        "    \"price\" : 110," +
                        "    \"type\" : null" +
                        "  }" +
                        "}";
    
    
                ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                Map<String, Person> mapPerson = mapper.readValue(s, MapPerson.class);
    
                Map<String, Person> filteredMap = Maps.filterValues(mapPerson, new Predicate<Person>() {
                    @Override
                    public boolean apply(Person person) {
                        return person.isNotEmpty();
                    }
                });
    
                System.out.println(filteredMap);
    
    
            }
    
            public static class MapPerson extends HashMap<String, Person> {}
    
            public static class Person{
    
                private int price;
                private String type;
    
                public Person() {
                }
    
                public boolean isNotEmpty() {
                    return !(0 == price && null ==type);
                }
    
                @Override
                public String toString() {
                    return "Person{" +
                            "price=" + price +
                            ", type='" + type + '\'' +
                            '}';
                }
    
                public int getPrice() {
                    return price;
                }
    
                public void setPrice(int price) {
                    this.price = price;
                }
    
                public String getType() {
                    return type;
                }
    
                public void setType(String type) {
                    this.type = type;
                }
            }
        }
    

    When you configure your objec mapper with configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) it will just add an empty instance of Person to your map instead of throwing an exception. So you should also define a method that will answer if an instance of Person is empty and then filter your map using it.

    If you use java 8 you can have a less code when you filter the map:

    Map<String, Person> filteredMap = Maps.filterValues(mapPerson, Person::isNotEmpty);
    

    BTW, it will work wine even you have some extra fields inside key values fro you JSON:

     {
          "code1" : {
            "price" : 100,
            "type" : null,
            "uselessExtraField": "Hi Stack"
          },
          "code2" : {
            "price" : 110,
            "type" : null,
            "anotherAccidentalField": "What?"
          }
        }
    

    You will have the same result as if that fields never existed.