繁体   English   中英

使用 GSON 转换嵌套的 JSON Stream

[英]Using GSON to transform a nested JSON Stream

目的:使用 GSON 获取大型 JSON 文件的输入 stream 并将其公开为迭代器的下游功能; 加上我物理上无法将整个 JSON 文件存储在 memory 中的约束。 目前,我确实使用一些基本的 Java 代码进行了这项工作,这些代码执行以下操作:

  • 知道什么时候跳过花括号
  • 读取 stream 直到找到下一个有效的 JSON Object
  • 使用 GSON 将其解析为 POJO

期望的结果看看 GSON 是否具有替换我自定义 Java 代码的内置功能。

样本输入文件

{
    "header":
    {
        "header1":"value1",
        "header2":"value2",
        "header3":"value3"
    },
    "body":
    {
        "obj-1":
        {
            "id":"obj-1",
            "name":"obj-1-name",
            "description":"obj-1-description"
        },
        "obj-2":
        {
            "id":"obj-2",
            "name":"obj-2-name",
            "description":"obj-2-description"
        },
        "obj-3":
        {
            "id":"obj-3",
            "name":"obj-3-name",
            "description":"obj-3-description"
        },
        "obj-4":
        {
            "id":"obj-4",
            "name":"obj-4-name",
            "description":"obj-4-description"
        }
    }
}

样品 OUTPUT 文档

{
    "header":
    {
        "header1":"value1",
        "header2":"value2",
        "header3":"value3"
    },  
    "object":
    {
        "id":"obj-1",
        "name":"obj-1-name",
        "description":"obj-1-description"
    }
}

POJO's have been created for the "header" object, the individual elements in the "body" JSON Object, and the output document.

使用以下内容作为初步解决问题的垫脚石, https://howtodoinjava.com/gson/jsonreader-streaming-json-parser/ ,我的理解是,由于存在 JSON 结构的转换,我需要执行基本的 3 步过程; 只是翻译成GSON具体功能?

如链接教程中所述,当您想以流方式处理 JSON 数据时,应使用JsonReader 然后,您可以使用从 Gson 获得的相应TypeAdapter实例用于您的 POJO 类,并使用它们来解析 header 和各个主体对象。
您也可以使用Gson.fromJson(JsonReader, ...)方法,而不是直接使用TypeAdapter实例; 然而 Gson 不幸的是不尊重宽大设置/总是让读者宽大 除非您明确需要这样做,否则我建议您不要这样做,而是直接使用TypeAdapter实例,因为这样会尊重JsonReader的宽松设置。

假设您有以下 POJO 类:

public class Header {
  public String header1;
  public String header2;
  public String header3;
}

public class BodyObject {
  public String id;
  public String name;
  public String description;
}

public class OutputDocument {
  public Header header;
  public BodyObject object;
}

然后,您可以创建一个创建Stream<OutputDocument>的方法,如下所示。 在此处使用Stream的优点是它的close方法可用于关闭提供 JSON 数据的Reader 但是,它也可以使用Iterator以类似的方式实现。

/**
 * Creates a {@link Stream} which transforms the data to {@link OutputDocument} elements.
 * 
 * <p><b>Important:</b> The provided reader will be closed by this method, or by the created
 * stream. It is therefore necessary to call {@link Stream#close()} (for example by using a
 * try-with-resources statement).
 * 
 * @param inputDocumentReader JSON data stream
 * @param gson Gson object used for looking up adapters
 * @return Stream of transformed elements
 */
public static Stream<OutputDocument> transform(Reader inputDocumentReader, Gson gson) throws IOException {
  JsonReader jsonReader = new JsonReader(inputDocumentReader);
  try {
    jsonReader.beginObject();
    String headerProperty = jsonReader.nextName();
    if (!headerProperty.equals("header")) {
      throw new IllegalArgumentException("Expected 'header' property at " + jsonReader.getPath());
    }

    // Read the Header
    TypeAdapter<Header> headerAdapter = gson.getAdapter(Header.class);
    Header header = headerAdapter.read(jsonReader);


    String bodyProperty = jsonReader.nextName();
    if (!bodyProperty.equals("body")) {
      throw new IllegalArgumentException("Expected 'body' property at " + jsonReader.getPath());
    }

    // Start reading body
    jsonReader.beginObject();
    TypeAdapter<BodyObject> bodyObjectAdapter = gson.getAdapter(BodyObject.class);

    long estimatedSize = Long.MAX_VALUE; // unknown size
    // Could also add `| NONNULL`, if there are no null body objects
    int characteristics = Spliterator.Spliterator.ORDERED;
    Spliterator<OutputDocument> spliterator = new AbstractSpliterator<>(estimatedSize, characteristics) {
      @Override
      public boolean tryAdvance(Consumer<? super OutputDocument> action) {
        try {
          // Check if end of 'body' object has been reached
          if (!jsonReader.hasNext()) {
            // End 'body'
            jsonReader.endObject();
            // End top level object
            jsonReader.endObject();

            if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
              throw new IllegalStateException("Expected end of JSON document at " + jsonReader.getPath());
            }
            // Reached end
            return false;
          } else {
            // Skip entry name
            jsonReader.skipValue();

            BodyObject object = bodyObjectAdapter.read(jsonReader);

            // Create combined OutputDocument
            OutputDocument result = new OutputDocument();
            result.header = header;
            result.object = object;

            action.accept(result);
            return true;
          }
        } catch (IOException e) {
          throw new UncheckedIOException("Failed reading next element", e);
        }
      }
    };
    return StreamSupport.stream(spliterator, false) // false, don't create parallel stream
        .onClose(() -> {
          try {
            jsonReader.close();
          } catch (IOException e) {
            throw new UncheckedIOException("Failed closing JsonReader", e);
          }
        });
  }
  catch (Exception e) {
    try {
      jsonReader.close();
    } catch (IOException suppressed) {
      e.addSuppressed(suppressed);
    }
    throw e;
  }
}

然后可以像这样调用此方法:

try (Stream<OutputDocument> stream = transform(inputDocumentReader, new Gson())) {
    ...
}

inputDocumentReader是您为 InputDocument 文件创建的Reader Gson实例可以是新实例(如上例所示),也可以是您使用GsonBuilder创建的实例,以防您注册自定义适配器以自定义 POJO 类或其字段的反序列化方式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM