gpt4 book ai didi

java - 如何使用 Jackson 根据运行时条件将集合序列化为空列表

转载 作者:行者123 更新时间:2023-12-02 10:15:16 25 4
gpt4 key购买 nike

我们有一个业务要求,如果用户没有查看子实体的权限,则 spring-boot 应用程序中的实体子集合(我们使用 JPA)的元素不应在 Rest api 中可见。

现在我们使用AOP来包装所有get我们服务中的方法,以便它们执行类似 if (!allowed("ChildView")) {entity.setChildren(new ArrayList<>())} 的操作由于某些原因,这对我来说似乎不是一个好的解决方案。首先,权限名称和集合 setter 之间的关系是在实体外部硬编码的。还修改实际对象,因为我们不想在 REST api 中显示有关它的内容,这似乎有点奇怪。如果您不想显示某些内容,则不会删除它。你可以隐藏它。所以我想为什么不在序列化时隐藏它呢?

所以我可以通过 Mixin 了解如何在运行时完全忽略属性和@JsonIgnore但我找不到如何返回空列表。

理想情况下,我喜欢这样的 API。

class Entity {
@OneToMany
@AuthSerialize("ChildView", default=Collections.emptyList())
Collection<Child> children;
}

当前的解决方案看起来像这样。

Map<Class<? extends BaseEntity>, Map<String, Consumer<BaseEntity>> protectors;

process(BaseEntity e) {
protectors.getOrDefault(e.getClass(), Collectoions.emptyMap())).forEach((permission, clearer) ->
if !allowed(permission) clearer.accept(e)
)

最佳答案

我认为“不浪费周期”是过度设计。如果您每秒序列化一百万个实体,这可能是一个有效的断言。否则JVM会为你优化“热点”。无论如何,这不会成为您的应用程序架构中的瓶颈。

如果您知道您的实体有一个共同的“子”数组字段,您可能需要应用相同的 JsonSerializer对他们所有人来说,只需维护 Map兼容的类。

你必须明白 jackson 有其自身的局限性。如果您需要更多的东西,您可能需要一个完全定制的解决方案。这是 jackson 能带来的最好的结果。

<小时/>

希望答案令人满意。
您可以使用自定义 JsonSerializer<T> .

class EntitySerializer extends StdSerializer<Entity> {
private static final long serialVersionUID = 1L;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

EntitySerializer() {
super(Entity.class);
}

@Override
public void serialize(
final Entity value,
final JsonGenerator generator,
final SerializerProvider provider) throws IOException {
final TreeNode jsonNode = OBJECT_MAPPER.valueToTree(value);

if (!AuthUtils.allowed("ChildView")) {
final TreeNode children = jsonNode.get("children");

if (children.isArray()) {
((ContainerNode<ArrayNode>) children).removeAll();
}
}

generator.writeTree(jsonNode);
}
}

但是,正如您所看到的,我们正在使用 ObjectMapper我们的 JsonSerializer 内的实例(或者您更喜欢用 JsonGenerator 手动“写入”每个字段?我不这么认为:P)。自 ObjectMapper寻找注释,为了避免序列化过程的无限递归,你必须放弃类注释

@JsonSerialize(using = EntitySerializer.class) 

并注册自定义JsonSerializer手动发送至 Jackson ObjectMapper .

final SimpleModule module = new SimpleModule();
module.setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(
final SerializationConfig config,
final BeanDescription beanDesc,
final JsonSerializer<?> serializer) {
final Class<?> beanClass = beanDesc.getBeanClass();
return beanClass == Entity.class ? new EntitySerializer() : serializer;
}
});

final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);

最后,您只需使用 ObjectMapper ,或者让您的框架使用它。
当您使用 Spring 时,您可以注册 @Bean类型为 ObjectMapper,标记为 @Primary ,或者您可以注册 @Bean类型 Jackson2ObjectMapperBuilder .

<小时/>

上一个答案。

作为allowed方法是静态的,这意味着它可以从“任何地方”访问。在对 jackson 进行了一番摆弄之后,我会给你两个选项中的第一个,因为我仍在研究第二个。

用注释你的类

@JsonSerialize(converter = EntityConverter.class)
public class Entity { ... }

您在此处指定自定义 Converter .

Converter实现非常简洁。
在静态 block 内,我只是得到 Auth注释值,但这是可选的,您可以做您认为最适合您的用例的事情。

class EntityConverter extends StdConverter<Entity, Entity> {
private static final String AUTH_VALUE;

static {
final String value;

try {
final Field children = Entity.class.getDeclaredField("children");
final AuthSerialize auth = children.getAnnotation(AuthSerialize.class);
value = auth != null ? auth.value() : null;
} catch (final NoSuchFieldException e) {
// Provide appropriate Exception, or handle it
throw new RuntimeException(e);
}

AUTH_VALUE = value;
}

@Override
public Entity convert(final Entity value) {
if (AUTH_VALUE != null) {
if (!AuthUtils.allowed(AUTH_VALUE)) {
value.children.clear();
}
}

return value;
}
}

请告诉我这是否足够,或者您更喜欢更复杂的解决方案。

关于java - 如何使用 Jackson 根据运行时条件将集合序列化为空列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54731635/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com