Использование условий для динамического исключения свойства POJO в сериализации Jackson json

У меня есть требование динамически исключать определенное свойство из списка определенного POJO. Основной POJO для сериализации:

public class Foo
{
    List<Bar> bar;

    public void setBar(List<Bar> bar)
    {
        this.bar = bar;
    }

    public List<Bar> getBar()
    {
        return this.bar;
    }

    public static class Bar
    {
        private int id;
        private boolean ignoreId;
        private String name;

        public void setId(int id)
        {
            this.id = id;
        }

        public int getId()
        {
            return this.id;
        }

        public void setIgnoreId(boolean ignoreId)
        {
            this.ignoreId = ignoreId;
        }

        public boolean isIgnoreId()
        {
            return this.ignoreId;
        }

        public void setName(String name)
        {
            this.name = name;
        }

        public String getName()
        {
            return this.name;
        }
    }
}

id игнорируется, если ignoreId = true, например:

[
    { "id": "1", "name": "one" },
    { "name": "two" }
    { "name": "three" }
    { "id": "4", "name": "four" }
]

На данный момент я пытался использовать JsonFilter и JacksonJsonViews, но не смог получить требуемый результат. Я был бы рад, если бы какой-либо указатель можно было дать для достижения этого.


person Damilola    schedule 01.09.2014    source источник
comment
В этом случае следует использовать JsonFilter. Не могли бы вы описать, почему фильтры не работали для вас?   -  person Alexey Gavrilov    schedule 01.09.2014
comment
@AlexeyGavrilov, я создал такую ​​логику: @JsonFilter("Exclude.Id") class PropertyFilterMixIn {} Если у меня есть Set<String> ignorableFieldNames = new ArrayList<String>().add("id");, я могу сделать: FilterProvider filters = new SimpleFilterProvider().addFilter("Exclude.Id", SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames)); ObjectWriter writer = mapper.writer(filters); System.out.println(writer.writeValueAsString(new Foo())); my challenge's to dynamically add id` для ignorableFieldNames[], если и только если ignoreId верно   -  person Damilola    schedule 01.09.2014


Ответы (3)


Вы должны написать собственный фильтр Джексона, который будет отфильтровывать POJO свойство в зависимости от другого значения свойства. Вы должны переопределить метод PropertyFilter.serializeAsField(), чтобы получить доступ к экземпляру сериализованного объекта. Вот пример:

public class JacksonFilter2 {
    @JsonFilter("filter")
    public static class Bar {
        public final int id;
        @JsonIgnore
        public final boolean ignoreId;
        public final String name;

        public Bar(int id, boolean ignoreId, String name) {
            this.id = id;
            this.ignoreId = ignoreId;
            this.name = name;
        }
    }

    public static class ExcludeIdFilter extends SimpleBeanPropertyFilter {

        @Override
        protected boolean include(BeanPropertyWriter writer) {
            return true;
        }

        @Override
        protected boolean include(PropertyWriter writer) {
            return true;
        }

        @Override
        public void serializeAsField(Object pojo,
                                     JsonGenerator jgen,
                                     SerializerProvider provider,
                                     PropertyWriter writer) throws Exception {
            if (pojo instanceof Bar
                    && "id".equals(writer.getName())
                    && ((Bar) pojo).ignoreId) {
               writer.serializeAsOmittedField(pojo, jgen, provider);
            } else {
                super.serializeAsField(pojo, jgen, provider, writer);
            }
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        List<Bar> bars = Arrays.asList(new Bar(1, false, "one"),  new Bar(2, true, "two"));
        ObjectMapper mapper = new ObjectMapper();
        mapper.setFilters(new SimpleFilterProvider().addFilter("filter", new ExcludeIdFilter()));
        System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(bars));
    }

}

Выход:

[ {
  "id" : 1,
  "name" : "one"
}, {
  "name" : "two"
} ]
person Alexey Gavrilov    schedule 01.09.2014
comment
Спасибо Алексей. Кажется, я получаю No filter configured with id... - person Damilola; 01.09.2014
comment
@Damiola, приведенный выше пример работает для меня. Какая у тебя версия Джексона? Можете ли вы обновить свой вопрос с кодом фильтра? - person Alexey Gavrilov; 01.09.2014
comment
похоже, моя проблема похожа на обсуждение с Тату здесь - person Damilola; 01.09.2014

Я использую Джексон 2.4.2.

Объект модели:

public class PostProject
{
    /* The field template of this project */
    private List< FieldTemplateObject > field_templates;

    /**
     * @author Damilola Okuboyejo
     */
    @JsonFilter( "FieldTemplateIdFilter" )
    public static class FieldTemplateObject
    {
      private int     id;
      private boolean is_inherited;
      private String  item_type;

      /**
       * @return the id
       */
      public int getId()
      {
         return id;
      }

      /**
       * @param id
       *           the id to set
       */
      public void setId( int id )
      {
         this.id = id;
      }

      /**
       * @return the is_inherited
       */
      public boolean isIs_inherited()
      {
         return is_inherited;
      }

      /**
       * @param is_inherited
       *           the is_inherited to set
       */
      public void setIs_inherited( boolean is_inherited )
      {
         this.is_inherited = is_inherited;
      }

      /**
       * @return the item_type
       */
      public String getItem_type()
      {
         if( item_type == null )
         {
            item_type = PostProject.item_type;
         }
         return item_type;
      }

      /**
       * @param item_type
       *           the item_type to set
       */
      public void setItem_type( String item_type )
      {
         this.item_type = item_type;
      }
   }
}

Сериализатор Myy выглядит так:

public static class ModelFieldSerializer extends SimpleBeanPropertyFilter
{
  @Override
  protected boolean include( BeanPropertyWriter writer )
  {
     return true;
  }

  @Override
  protected boolean include( PropertyWriter writer )
  {
     return true;
  }

  @Override
  public void serializeAsField( Object pPojo, JsonGenerator pJgen,
        SerializerProvider pProvider, PropertyWriter pWriter )
        throws Exception
  {
     if( pPojo instanceof FieldTemplateObject )
     {
        if( ("id".equals( pWriter.getName() ))
              && ((FieldTemplateObject) pPojo).isIs_inherited() )
        {
           pWriter.serializeAsOmittedField( pPojo, pJgen, pProvider );
        }
        else
        {
           super.serializeAsField( pPojo, pJgen, pProvider, pWriter );
        }
     }
  }
}

ПРИМЕЧАНИЕ. Класс модели передается по сети в среде клиент-серверной архитектуры env.

person Damilola    schedule 01.09.2014
comment
Не могли бы вы показать, как вы регистрируете свой фильтр для ObjectMapper? Также имеет смысл переместить код в вопрос, поскольку это не фактический ответ. - person Alexey Gavrilov; 02.09.2014
comment
вы используете ObjectMapper до или после настройки фильтра? Можете ли вы создать полный класс, воспроизводящий вашу проблему? Код из моего ответа работает для вас? - person Alexey Gavrilov; 02.09.2014
comment
Это довольно подробный код. Комментарий, который я указал ранее в разделе ответов, должен воспроизвести проблему. Я заметил, что как только я отмечаю FieldTemplateObject с помощью @JsonFilter("FieldTemplateIdFilter" ), я получаю исключение. Согласно документу Джексона 2.3+, я заметил, что свойство или геттер также могут быть аннотированы с помощью @JsonFilter. Если я попробую это, проблема исчезнет, ​​но фильтрация не произойдет. - person Damilola; 02.09.2014

Я только что видел преступника. Большое спасибо @Alexey за то, что остался со мной в этом. После долгих попыток я обнаружил, что Jackson 2.4.2 мог потребовать, чтобы я поместил свой сериализатор в модель, с которой он будет работать. Интересно, почему, но, вероятно, из-за требований сериализации объектов Java Джексону не удавалось сопоставить фильтр во время выполнения. Надеюсь, Тату отметит это в документации Джексона.

public class PostProject
{
    /* The field template of this project */
    private List< FieldTemplateObject > field_templates;

    /**
     * @author Damilola Okuboyejo
     */
    @JsonFilter( "FieldTemplateIdFilter" )
    public static class FieldTemplateObject
    {
      private int     id;
      private boolean is_inherited;
      private String  item_type;

      /**
       * @return the id
       */
      public int getId()
      {
         return id;
      }

      /**
       * @param id
       *           the id to set
       */
      public void setId( int id )
      {
         this.id = id;
      }

      /**
       * @return the is_inherited
       */
      public boolean isIs_inherited()
      {
         return is_inherited;
      }

      /**
       * @param is_inherited
       *           the is_inherited to set
       */
      public void setIs_inherited( boolean is_inherited )
      {
         this.is_inherited = is_inherited;
      }

      /**
       * @return the item_type
       */
      public String getItem_type()
      {          
         return item_type;
      }

      /**
       * @param item_type
       *           the item_type to set
       */
      public void setItem_type( String item_type )
      {
         this.item_type = item_type;
      }
   }

   public static class ModelFieldSerializer extends SimpleBeanPropertyFilter
   {
      @Override
      protected boolean include( BeanPropertyWriter writer )
      {
         return true;
      }

      @Override
      protected boolean include( PropertyWriter writer )
      {
         return true;
      }

      @Override
      public void serializeAsField( Object pPojo, JsonGenerator pJgen,
            SerializerProvider pProvider, PropertyWriter pWriter )
            throws Exception
      {
         if( pPojo instanceof FieldTemplateObject )
         {
             boolean vAllowId = ((FieldTemplateObject) pPojo).isIs_inherited();
             if( !vAllowId && ("id".equals( pWriter.getName() )) )
             {
               return; // skip the id property
             }
             else
             {
               super.serializeAsField( pPojo, pJgen, pProvider, pWriter );
             }
         }
      }
    }
}
person Damilola    schedule 02.09.2014
comment
Требования к сериализации объектов Java не применяются к Jackson. Вы должны были столкнуться с чем-то другим. Если вы можете опубликовать минимизированный воспроизводимый пример кода на GitHub, я попытаюсь проследить за этим, иначе я понятия не имею. - person Alexey Gavrilov; 02.09.2014
comment
@Alexey Я только что разместил уменьшенную воспроизводимую версию на GitHub - person Damilola; 02.09.2014