当前位置: 首页 > 知识库问答 >
问题:

带有条件隐藏成员的Jackson自定义序列化程序生成无效的JSON

锺宜
2023-03-14

我需要创建一个自定义序列化程序,有条件地跳过字段。与使用jackson序列化时有条件跳过对象中描述的情况不同,我的类包含一个POJO成员。PersonalInfo有一个成员地址。如果地址被隐藏,结果JSON仍然有“Address”标记,但没有值。我想不出怎么解决这个问题。

在ObjectMapper上创建自定义序列化程序(参见3. athttp://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)));
    }

}

更新:感谢反馈,我现在有一个基于@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,地址:{城市:A,街道:B" } }, { }, { "姓名:玛丽,年龄:40,地址": { } }, { "城市:A,“街道”:“B" }, { } ]

预期:

[{姓名:约翰,年龄:30,地址:{城市:A,街道:B" } }, { "姓名:玛丽,年龄:40, }, { "城市:A,街道: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);
        }

    }
}

受这个问题启发的解决方案:如何使用Jackson删除Java中的空json节点?

共有1个答案

谭成业
2023-03-14

序列化程序已损坏:如果请求,它无法选择不写入值。在写入属性值的情况下,调用方已经写出了属性名,所以不写入该值确实会中断输出。这要么导致抛出异常(理想情况下),要么导致输出中断(不理想情况下);不管怎样,JsonSerializer都不允许尝试决定是否正在写入值。

要排除正在序列化的属性,有效选项包括:

  1. 静态属性注释,比如总是排除特定命名属性的@JsonIgnore@JsonIgnoreProperties
 类似资料:
  • 下面的代码再现了这个问题: 上面的代码不做其他注册“自定义”序列化程序的事情(只是委托回原始序列化程序),但它生成的JSON没有null属性: {“第一个”:“鲍勃”,“最后一个”:“巴克”} 我读过许多看似相关的SO文章,但没有一篇能让我找到解决方案。我尝试在序列化时显式地将映射器设置为,但没有成功。 我唯一的线索是JavaDoc for JsonSerializer中的一条注释: 注意:永远不

  • 情况如下:我已经设法让Jackson反序列化以下通用 作为HTTP客户端,并使用exchange

  • 我想将包含另一个空值POJO的POJO序列化为JSON。 例如,给定: 连载之后,它看起来会是这样的 如果类的所有字段也为空,是否可以排除空对象?理想情况下,全局适用于每个对象,而无需编写自定义代码。

  • 如果类的所有字段也为空,是否可以排除空对象?理想情况下,全局地用于每个对象,而无需编写自定义代码。

  • 我试图创建会影响序列化值的自定义jackson注释。 意思是: 现在序列化对象X(10)将导致: 我怎样才能做到这一点?

  • 我尝试在包含带有注释的对象列表的列表上应用自定义序列化程序。如果未授予用户权限(角色与用户不同),我想删除这些属性。当属性不是列表时,它工作得很好(https://www.baeldung.com/jackson-serialize-field-custom-criteria) **更新** 我的第一个方法是为mapper注册一个新模块 和我的自定义序列化程序 但我无法访问列表中包含的对象。我想定