【问题标题】:Custom BeanPropertyFilter - only serializing part of string自定义 BeanPropertyFilter - 仅序列化部分字符串
【发布时间】:2014-10-08 06:49:44
【问题描述】:

我急需帮助。我目前正在对 Content API 中的资源进行一些安全限制,我需要:包括属性、截断属性(如果它们是 String.class)或从序列化对象中删除属性,基于 SecurityContext。

我使用 Jackson 作为我的 json 序列化器,并选择使用 BeanPropertyFilter 来检查每个属性是否应该序列化。

现在删除和包含选项非常简单,无论我是否序列化 bean。

但截断选项有点糟糕。所以这是我到目前为止关于属性序列化所做的工作。

@Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception {
    switch (determineFilterAction(writer)) {
        case INCLUDE_UNMODIFIED:
            writer.serializeAsField(bean, jgen, provider);
            break;
        case INCLUDE_TRUNCATED:
            Object value = writer.get(bean);
            if (!(value instanceof String)) {
                throw new UnsupportedOperationException("Annotation indicates truncate on " + writer.toString() + " which is not a string, but a " + value.getClass().getSimpleName() + " and is unsupported");
            }
            JsonSerializer oldSerialzer= writer.getSerializer();
            writer.assignSerializer(new TruncateStringJsonSerializer());
            writer.serializeAsField(bean, jgen, provider);
            writer.assignSerializer(oldSerialzer);
            break;
        case REMOVE:
            // do nothing
            break;
    }
}

问题是,BeanPropertyWriter.assignSerializer() 方法中有一个保护,不允许我为该属性切换新的自定义序列化程序。

有没有办法挂钩和修改 bean-value?或者我是否必须重写工厂和 BeanPropertyWriter 才能重写 assignSerializer() 方法(以避免序列化程序保护)?

我不能只用一个特殊的 writer 来注释我的属性,因为序列化的对象对 SecurityContext 一无所知,所以没有办法将 securityContext 注入到自定义序列化程序中。

有没有办法让这个截断选项发生(不会让它太老套)?

提前致谢。

//最好的问候,马丁。

更新

根据 StaxMan 的回答,我已将方法更新为如下所示,并且似乎有效。至少我所有的测试都能正常运行。将在开发应用服务器上进一步测试。

@Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception {
    switch (determineFilterAction(writer)) {
        case INCLUDE_UNMODIFIED:
            writer.serializeAsField(bean, jgen, provider);
            break;
        case INCLUDE_TRUNCATED:
            Object value = writer.get(bean);
            if (!(value instanceof String)) {
                throw new UnsupportedOperationException("Annotation indicates truncate on " + writer.toString() + " which is not a string, but a " + value.getClass().getSimpleName() + " and is unsupported");
            }
            String valueString = (String) value;
            jgen.writeFieldName(writer.getName());
            jgen.writeString(StringUtils.abbreviate(valueString, MAX_LENGTH));
            break;
        case REMOVE:
            // do nothing
            break;
    }
}

【问题讨论】:

  • 如果我可以用我自己的方法覆盖杰克逊初始化的默认 BeanPropertyWriter 并注册它,它可以覆盖阻止我切换序列化程序的保护。但我在网上找不到任何人是如何做到这一点的。 (至少在没有实施自己的工厂供应商和自己的工厂的情况下并非如此)。我不太喜欢。

标签: java json serialization jackson java-custom-serialization


【解决方案1】:

不,甚至不要尝试调用assignSerializer:这在多线程环境中是个坏主意——该序列化程序只有一个实例,即使你可以调用它,它也会导致在同时进行两个序列化的情况下,你是一个奇怪的例外。

但也许你应该考虑从不同的方向来解决这个问题:既然你已经找到了String 值,为什么不直接使用JsonGenerator 写入截断值呢? 或者,如果您愿意,可以委托编写的东西;但既然你在调用它,它就不必是 JsonSerializer 或任何东西。

【讨论】:

  • 感谢您的回复。这有帮助,而且奏效了。它有点难看,只是手动编写,我宁愿让杰克逊设置处理这一切。很高兴了解assignSerializer,我认为杰克逊可以处理这个问题,当它可以使用@JsonSerialize(using = SomeCustomSerializer.class) 处理切换序列化器时。源代码中甚至还有一条注释,评论天气或不删除在未来的版本中守护。 :)
  • 是的,并不总是很清楚可以做什么,尤其是应该做什么。也许该方法应该有一个警告说明。加上基本的JsonSerializerJsonDeserializer 应该强调实例需要是线程安全的,因为实例是共享的以供并发使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-23
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2011-10-22
  • 1970-01-01
相关资源
最近更新 更多