使用 Faster Jackson 反序列化数组时设置最大大小

Setting a max size when using Faster Jackson to deserialize an array

我在下面的代码中使用包含自定义对象数组的对象反序列化 JSON 字符串。我们正在使用 fasterxml.jackson。我们已经看到数组中的项目数量大到足以在反序列化时导致 OutOfMemoryError 的情况。对可以从数组中序列化的项目数量强制执行最大大小的最简单方法是什么?我真的很想在超出限制的情况下抛出异常或 return 错误。

class InputMessage {
    private final List<Action> actions;
    
    @JsonCreator
    public InputMessage(@JsonProperty("actions") final List<actions> actions) {
        this.actions = actions;
    }

    List<Action> public getActions() {
        return actions;
    }
}

到目前为止我看到的所有解决方案都在完成序列化到 java 对象后对项目列表执行大小检查。我很想知道在序列化期间如何完成它,所以不要在 JVM 上消耗过多的内存。

@JsonCreator@JsonProperty 注释是绑定 API 的一部分,它实现并使用自己的解析逻辑,不允许您想要的自定义条件。您需要覆盖部分解析。

对于要限制的数组,使用自定义反序列化器。您将希望扩展 StdDeserializer, and implement the deserialize method. This method will make use of Jackson's Streaming API 以处理 JSON 的实际标记,如 {["some string",并且您将负责分配对象(或不).

deserialize 方法中,您不必手动解析 Action 对象;您仍然可以通过创建 ObjectMapper 然后使用 objectMapper.readValue(jsonParser, Action.class).

来依赖绑定 API

通过 JsonDeserialize:

引用您的自定义解串器 class
public InputMessage(
    @JsonProperty("actions") 
    @JsonDeserialize(using = /* your class */)
    final List<actions> actions) {

示例和指南的一些搜索:

这是我用来解决问题的方法。该代码具有出色的性能,因为它在解析输入时检查限制,而不是实例化所有项目,并且可能 运行 内存不足。这可以防止单个 JSON 字符串不成比例地消耗更多内存。

/**
 * Custom deserializer used to convert a list of {@link Input}
 * into a list of {@link ConvertedInput}.
 */
static final class RequestItemsDeserializer extends StdDeserializer<List<ConvertedInput>> {

    // RFC 1149.5
    private static final long serialVersionUID = 4L;
    private static final int INPUT_LIST_LIMIT = 1024;

    private static final ObjectReader READER = new ObjectMapper().readerFor(Input.class);

    public RequestItemsDeserializer() {
        this(List.class);
    }

    public RequestItemsDeserializer(Class<?> clazz) {
        super(clazz);
    }

    @Override
    public List<ConvertedInput> deserialize(final JsonParser jp, final DeserializationContext ctxt)
        throws IOException, JsonProcessingException {

        final List<ConvertedInput> convertedInput = new ArrayList<>();
        if (!jp.isClosed() && JsonToken.START_ARRAY == jp.getCurrentToken()) {
            for (int i = 1; !jp.isClosed() && (jp.nextToken() != JsonToken.END_ARRAY); i++) {
                if (i > INPUT_LIST_LIMIT) {
                    throw new RequestInputTooLargeException("\"input\":[...]",
                            "This request exceeded the item limit of " + INPUT_LIST_LIMIT + ".");
                }
                convertedInput.add(READER.readValue(jp));
            }
        }
        return convertedInput;
    }
}