2015-06-29 4 views
0

Мне нужно создать собственный сериализатор, который условно пропускает поля. В отличие от случая, описанного в Skip objects conditionally when serializing with jackson В моем классе содержится член POJO. Личный кабинет имеет адрес в качестве участника. В случае, если адрес скрыт, в результате JSON по-прежнему имеет тег «адрес», но без значения. Я не мог понять, как это исправить.Пользовательский сериализатор Jackson с условно скрытыми элементами создает недопустимый JSON

Создание настраиваемого сериализатора на ObjectMapper (см. 3. на http://www.baeldung.com/jackson-custom-serialization) приводит к точному результату.

Вот адаптированный код из ссылочного вопрос, который показывает проблему:

public class JacksonHide { 
    @JsonIgnoreProperties("hidden") 
    public static interface IHideable { 
     boolean isHidden(); 
    } 

    public static class Address implements IHideable { 
     public final String city; 
     public final String street; 
     public final boolean hidden; 

     public Address(String city, String street, boolean hidden) { 
      this.city = city; 
      this.street = street; 
      this.hidden = hidden; 
     } 

     @Override 
     public boolean isHidden() { 
      return hidden; 
     } 
    } 

    public static class PersonalInfo implements IHideable { 
     public final String name; 
     public final int age; 
     public final Address address; 
     public final boolean hidden; 

     public PersonalInfo(String name, int age, Address address, boolean hidden) { 
      this.name = name; 
      this.age = age; 
      this.address = address; 
      this.hidden = hidden; 
     } 

     @Override 
     public boolean isHidden() { 
      return hidden; 
     } 
    } 

    private static class MyBeanSerializerModifier extends BeanSerializerModifier { 
     @Override 
     public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) { 
      if (IHideable.class.isAssignableFrom(beanDesc.getBeanClass())) { 
       return new MyIHideableJsonSerializer((JsonSerializer<IHideable>) serializer); 
      } 
      return super.modifySerializer(config, beanDesc, serializer); 
     } 

     private static class MyIHideableJsonSerializer extends JsonSerializer<IHideable> { 
      private final JsonSerializer<IHideable> serializer; 

      public MyIHideableJsonSerializer(JsonSerializer<IHideable> serializer) { 
       this.serializer = serializer; 
      } 

      @Override 
      public void serialize(IHideable value, JsonGenerator jgen, SerializerProvider provider) throws IOException { 
       if (!value.isHidden()) { 
        serializer.serialize(value, jgen, provider); 
       } 

      } 
     } 
    } 

    public static void main(String[] args) throws JsonProcessingException { 
     ObjectMapper mapper = new ObjectMapper(); 
     SimpleModule module = new SimpleModule(); 
     module.setSerializerModifier(new MyBeanSerializerModifier()); 
     mapper.registerModule(module); 

     PersonalInfo p1 = new PersonalInfo("John", 30, new Address("A", "B", false), false); 
     PersonalInfo p2 = new PersonalInfo("Ivan", 20, new Address("C", "D", true), true); 
     PersonalInfo p3 = new PersonalInfo("Mary", 40, new Address("C", "D", true), false); 
     Address a1 = new Address("A", "B", false); 
     Address a2 = new Address("C", "D", true); 

     System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(Arrays.asList(p1, p2, p3, a1, a2))); 
    } 

} 

UPDATE: Благодаря обратной связи у меня сейчас вариант, основанный на @JSONFilter, что дает мне по крайней мере, действительный JSON. К сожалению, узлы все еще существуют, но теперь пустые ({}). Как я могу полностью избавиться от них?

public class JacksonFilterHide { 

    @JsonFilter("HiddenFilter") 
    @JsonIgnoreProperties("hidden") 
    public static interface IHideable { 
     boolean isHidden(); 
    } 

    public static class Address implements IHideable { 
     public final String city; 
     public final String street; 
     public final boolean hidden; 

     public Address(String city, String street, boolean hidden) { 
      this.city = city; 
      this.street = street; 
      this.hidden = hidden; 
     } 

     @Override 
     public boolean isHidden() { 
      return hidden; 
     } 
    } 

    public static class PersonalInfo implements IHideable { 
     public final String name; 
     public final int age; 
     public final Address address; 
     public final boolean hidden; 

     public PersonalInfo(String name, int age, Address address, boolean hidden) { 
      this.name = name; 
      this.age = age; 
      this.address = address; 
      this.hidden = hidden; 
     } 

     @Override 
     public boolean isHidden() { 
      return hidden; 
     } 
    } 

    static final PropertyFilter hiddenFilter = new SimpleBeanPropertyFilter() { 
     @Override 
     public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception { 
      if (include(writer)) { 
       if (pojo instanceof IHideable && ((IHideable) pojo).isHidden()) { 
        return; 
       } else { 
        writer.serializeAsField(pojo, jgen, provider); 
        return; 
       } 
      } else if (!jgen.canOmitFields()) { // since 2.3 
       writer.serializeAsOmittedField(pojo, jgen, provider); 
      } 
     } 

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

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

    public static void main(String[] args) throws JsonProcessingException { 
     ObjectMapper mapper = new ObjectMapper(); 
     // ObjectMapper mapper = UserInteractionModel.getMapper(); 
     FilterProvider filters = new SimpleFilterProvider().addFilter("HiddenFilter", hiddenFilter); 
     mapper.setFilters(filters); 
     mapper.enable(SerializationFeature.INDENT_OUTPUT); 

     PersonalInfo p1 = new PersonalInfo("John", 30, new Address("A", "B", false), false); 
     PersonalInfo p2 = new PersonalInfo("Ivan", 20, new Address("C", "D", true), true); 
     PersonalInfo p3 = new PersonalInfo("Mary", 40, new Address("C", "D", true), false); 
     Address a1 = new Address("A", "B", false); 
     Address a2 = new Address("C", "D", true); 

     System.out.println(mapper.writeValueAsString(Arrays.asList(p1, p2, p3, a1, a2))); 
    } 

} 

Выход сейчас:

[{ "Имя": "Джон", "возраст": 30, "адрес": { "город": "А", «улица ":" B "}}, {}, {" name ":" Mary "," age ": 40," address ": {}}, {" city ":" A "," street ":" B " }, {}]

Ожидаемое:

[{"name": "John", "age": 30, "address": { "city": "A", "street": "B"}}, {"name": "Mary" , "возраст": 40}, { "город": "а", "улица": "B"}]

Update2 Временное исправление путем обхода дерева и удаление пустых узлов. Уродливо, но работает пока. Все еще ищет лучший ответ.

private void removeEmptyNodes(JSONObject json) { 
    Iterator<String> iter = json.keys(); 
    while (iter.hasNext()) { 
     String key = iter.next(); 
     JSONObject node; 
     try { 
      node = json.getJSONObject(key); 
     } catch (JSONException e) { 
      continue; 
     } 
     if (node.length() == 0) { 
      iter.remove(); 
     } else { 
      removeEmptyNodes(node); 
     } 

    } 
} 

Решение вдохновлен этим вопросом: How do I remove empty json nodes in Java with Jackson?

+0

Избавление от пустых объектов является немного сложнее, частично, так как, хотя можно включить исключение «пустых» объектов (это обнаруживается с помощью 'JsonSerializer. isEmpty (...) '), это не применяется к массивам или' List'. – StaxMan

ответ

1

Ваш сериализатору сломана: он не может выбрать НЕ ПИШИТЕ значение, если требуется. В случае написания значения свойства вызывающий абонент уже выписал имя свойства, поэтому не записывать значение действительно приведет к выходу из строя. Это либо приводит к тому, что исключение выбрано (идеально), либо сломанный выход (менее идеально); независимо от того, JsonSerializer не может попытаться решить, записывается ли значение.

Чтобы исключить свойство сериализации, ваши действительные варианты включают:

  1. Статических аннотации собственности как @JsonIgnore и @JsonIgnoreProperties, которые всегда исключают конкретное именованное свойство
  2. статических аннотации @JsonInclude, что обосновывает включение по типу значения (нет nulls, no absent, no empty values)
  3. Статическое определение, но динамический выбор @JsonView для использования (наборы свойств, которые вы можете динамически исключать путем сопоставления с представлением)
  4. Dynamic @JsonFilter
  5. Пользовательские сериализатору для типа, который содержит свойству
+0

Спасибо. @JsonFilter дал мне небольшой прогресс, но я все еще не закончил. См. Обновление в исходном вопросе. – konfusius

Смежные вопросы