【问题标题】:Reading skipped children in Jackson Custom Deserializer在 Jackson Custom Deserializer 中阅读跳过的孩子
【发布时间】:2015-02-12 01:30:58
【问题描述】:

我有一个特定类的反序列化器,它在读取字段时需要一些排序。 假设我的班级有两个字段(field1field2),为了阅读 field2,首先需要 field1

例如,对于以下 json 数据,它可以工作,因为当反序列化器解析 field2 时,field1 已经设置:

{"field1": 3, "field2": 4}

但是,如果我们反转字段:

{"field2": 4, "field1": 3}

我需要通过jp.skipChildren 跳过field2,因为field1 未设置。当field1被解析时,Jackson应该重新读取并解析field2

一种选择是解析 field2 而不是跳过并将其保存在一个变量中,以便在设置 field1 时,它可以使用保存 field2 中数据的变量。然而;基于field1 的值,我可能不需要解析field2,因此我正在寻找更好的解决方案,因为性能在这部分代码中至关重要。

我正在使用Mapper.readValue(byte[], MyClass.class) 方法,似乎杰克逊使用ReaderBasedJsonParser 进行解析。即使可以获得令牌位置,我也找不到设置令牌位置的方法。

【问题讨论】:

  • 您有一个问题,即您依赖 RFC for JSON (RFC 7159) 所说的对象成员顺序无关紧要。你为什么要订单?为什么不尝试反序列化两者?
  • @fge 因为特定字段有关于我应该如何解析其他字段的信息。将 JSON 树解析为通用 JsonNode 始终是一种选择,但它会带来开销,所以我想避免它。
  • @fge,既然您了解 Avro,我想做的就是将 JSON 解析为 Avro 模型。 JSON 中的一些字段包含有关 Avro 模式的信息,因此为了解析树,首先我需要知道这些值。您可以从这里查看代码:github.com/buremba/rakam/blob/…

标签: java json jackson deserialization jackson-modules


【解决方案1】:

我终于找到了一种方法。这实际上是一种解决方法,但它通过了我编写的测试。 当您将字节数组传递给 mapper.readValue 时,它​​使用 ReaderBasedJsonParser 迭代数组并解析 JSON 树。

public static class SaveableReaderBasedJsonParser extends ReaderBasedJsonParser {
    private int savedInputPtr = -1;

    public SaveableReaderBasedJsonParser(IOContext ctxt, int features, Reader r, ObjectCodec codec, CharsToNameCanonicalizer st, char[] inputBuffer, int start, int end, boolean bufferRecyclable) {
        super(ctxt, features, r, codec, st, inputBuffer, start, end, bufferRecyclable);
    }

    public void save() {
        savedInputPtr = _inputPtr;
    }

    public boolean isSaved() {
        return savedInputPtr>-1;
    }

    public void load() {
        _currToken = JsonToken.START_OBJECT;
        _inputPtr = savedInputPtr;
        _parsingContext = _parsingContext.createChildObjectContext(0, 0);
    }
}

当您使用此 JsonParser 时,将传递给您的反序列化器 EventDeserializer.deserialize(JsonParser, DeserializationContext) 的 JsonParser 实例将是 SaveableReaderBasedJsonParser,因此您可以安全地转换它。

当你想保存仓位的时候,打电话jp.save(),这样当你需要回去的时候,你可以直接打电话给jp.load()

正如我所说,这实际上是一种解决方法,但是当您需要这种功能并且出于性能原因不想两次解析树时,您可以尝试一下。

【讨论】:

    【解决方案2】:

    自定义反序列化器需要使用流式 API。没有办法回去,重新解析等。

    您是否为字段类型或包含需要这种特殊处理的字段的类注册了自定义反序列化器?

    如果您为包含这些字段的类注册反序列化器,您可以只使用流式 API,读取实例的所有字段,临时存储它们,例如在 HashMap 中,然后分配值。

    顺便说一句:你的问题闻起来像 XYProblem。也许你应该发布另一个关于原因的问题,你需要在这里解决这个问题,看看是否有更好的方法。

    【讨论】:

    • 我找到了一种使用自定义 JsonParser 的方法。不幸的是,HashMap 带来了开销,正如我所说,性能对于这部分应用程序至关重要。
    猜你喜欢
    • 1970-01-01
    • 2020-06-09
    • 1970-01-01
    • 2011-02-04
    • 2017-03-04
    • 2013-07-01
    • 1970-01-01
    • 2021-11-27
    • 1970-01-01
    相关资源
    最近更新 更多