首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用GSON转换嵌套的JSON流

使用GSON转换嵌套的JSON流
EN

Stack Overflow用户
提问于 2021-11-24 16:00:33
回答 1查看 323关注 0票数 0

用途:使用GSON获取一个大型JSON文件的输入流,并将其作为Iterator向下游函数公开;添加的约束使我无法将整个JSON存储在内存中。目前,我确实使用了一些基本的Java代码来完成以下工作:

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

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

示例输入文档

代码语言:javascript
复制
{
    "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"
        }
    }
}

示例输出文档

代码语言:javascript
复制
{
    "header":
    {
        "header1":"value1",
        "header2":"value2",
        "header3":"value3"
    },  
    "object":
    {
        "id":"obj-1",
        "name":"obj-1-name",
        "description":"obj-1-description"
    }
}

POJO是为"header“对象、"body”JSON对象中的各个元素以及输出文档创建的。

使用下面的垫脚石作为初始解决问题的跳板,https://howtodoinjava.com/gson/jsonreader-streaming-json-parser/,我的理解是,由于JSON结构有一个转换,我需要完成这个基本的3步过程,只需将它转换为GSON特定的函数即可?

EN

回答 1

Stack Overflow用户

发布于 2021-11-26 15:25:22

正如链接教程中提到的,当您想要以流的方式处理JSON数据时,应该使用JsonReader。然后,您可以为POJO类使用从Gson获得的相应的TypeAdapter实例,并使用它们解析标头和单个body对象。

您还可以使用Gson.fromJson(JsonReader, ...)方法,而不是直接使用TypeAdapter实例;但是,很不幸,Gson不尊重宽大处理设置/总是使读者宽厚。除非您明确需要这样做,否则我建议不要这样做,而是直接使用TypeAdapter实例,因为这样就尊重了JsonReader的宽大设置。

假设您有以下POJO类:

代码语言:javascript
复制
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以类似的方式实现。

代码语言:javascript
复制
/**
 * 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;
  }
}

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

代码语言:javascript
复制
try (Stream<OutputDocument> stream = transform(inputDocumentReader, new Gson())) {
    ...
}

inputDocumentReader是为您的InputDocument文件创建的ReaderGson实例可以是一个新实例(如上面的示例所示),也可以是您用GsonBuilder创建的实例,以便注册自定义适配器来自定义POJO类或它们的字段如何反序列化。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70099221

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档