我有下面的代码,其中我正在用一个包含自定义对象数组的对象反序列化JSON字符串。我们使用的是fasterxml.jackson。我们已经看到过这样的情况:数组中的项数足够大,以至于在反序列化时会导致OutOfMemoryError
。对可以从数组中序列化的项目数量强制执行最大大小的最简单方法是什么?我真的希望在超过限制的情况下抛出异常或返回错误。
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
,并实现deserialize
方法。此方法将使用Jackson的Streaming API来处理JSON的实际令牌,如{
或[
或"some string"
,您将负责分配对象(或不分配对象(。
在deserialize
方法中,您不必手动解析Action
对象;您仍然可以通过创建ObjectMapper
然后使用objectMapper.readValue(jsonParser, Action.class)
来依赖绑定API。
通过JsonDeserialize:参考您的自定义反序列化程序类
public InputMessage(
@JsonProperty("actions")
@JsonDeserialize(using = /* your class */)
final List<actions> actions) {
一些搜索示例和指南:
- Jackson"自定义解串器"JsonDeserialize">
- Jackson JsonParser反序列化数组
- Jackson";jsonParser.nextToken(("END_ARRAY
以下是我用来解决这个问题的方法。该代码具有很好的性能,因为它在分析输入时会检查限制,而不是实例化所有项并可能耗尽内存。这可以防止单个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;
}
}