是否可以将java.util.Stream传递给Gson?

时间:2018-06-15 19:13:36

标签: json stream gson java-stream

我目前正在开展一个项目,我需要从Database获取大量数据并将其解析为特定的 Json格式,当我将List传递给Gson时,我已经构建了自定义序列化程序并且它正常工作。但由于我已经在Streams Layer中使用JPA,我认为我可以将Stream传递给Gson解析器,以便它可以将其直接转换为我的Json数据。但是我得到一个空的Json对象而不是正确填充的对象。

所以,如果有人能指出我GsonJava 8 Streams合作的方法,或者目前无法做到这一点......我在 Google上找不到任何东西,所以我来到 Stackoverflow

2 个答案:

答案 0 :(得分:1)

您可以使用JsonWriter将数据流式传输到输出流:

public void writeJsonStream(OutputStream out, Stream<DataObject> data) throws IOException {
     try(JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"))) {
          writer.setIndent("    ");
          writer.beginArray();
          data.forEach(d -> {
              d.beginObject();
              d.name("yourField").value(d.getYourField());
              ....
              d.endObject();
          });
          writer.endArray();
     }
}

请注意,您负责控制json结构。

也就是说,如果您的DataObject包含嵌套对象,则必须分别编写beginObject()/endObject()。嵌套数组也是如此。

答案 1 :(得分:1)

它并不像人们期望的那样微不足道,但它可以通用的方式完成。

当您查看Javadoc到TypeAdapterFactory时,它们为自定义类型提供了一种非常简单的TypeAdapterFactory编写方式。唉,由于元素类型检测的问题,它无法按预期工作。可以在Gson-internal CollectionTypeAdapterFactory中找到执行此操作的正确方法。这是非常复杂的,但采取必要的一个可以得出类似的东西:

final class StreamTypeAdapterFactory implements TypeAdapterFactory {

  @SuppressWarnings("unchecked")
  @Override
  public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
    Type type = typeToken.getType();
    Class<? super T> rawType = typeToken.getRawType();

    if (!Stream.class.isAssignableFrom(rawType)) {
      return null;
    }

    Type elementType = ExtraGsonTypes.getStreamElementType(type, rawType);
    TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
    return (TypeAdapter<T>) new StreamTypeAdapter<>(elementAdapter);
  }

  private static class StreamTypeAdapter<E> extends TypeAdapter<Stream<E>> {

    private final TypeAdapter<E> elementAdapter;

    StreamTypeAdapter(TypeAdapter<E> elementAdapter) {
      this.elementAdapter = elementAdapter;
    }

    public void write(JsonWriter out, Stream<E> value) throws IOException {
      out.beginArray();
      for (E element : iterable(value)) {
        elementAdapter.write(out, element);
      }
      out.endArray();
    }

    public Stream<E> read(JsonReader in) throws IOException {
      Stream.Builder<E> builder = Stream.builder();
      in.beginArray();
      while (in.hasNext()) {
        builder.add(elementAdapter.read(in));
      }
      in.endArray();
      return builder.build();
    }
  }

  private static <T> Iterable<T> iterable(Stream<T> stream) {
    return stream::iterator;
  }
}

ExtraGsonTypes是一个特殊的类,我用来绕过对$Gson$Types.getSupertype方法的包私有访问。如果您不使用JDK 9的模块,这是一个有效的黑客 - 您只需将此类放在与$Gson$Types相同的包中:

package com.google.gson.internal;

import java.lang.reflect.*;
import java.util.stream.Stream;

public final class ExtraGsonTypes {

  public static Type getStreamElementType(Type context, Class<?> contextRawType) {
    return getContainerElementType(context, contextRawType, Stream.class);
  }

  private static Type getContainerElementType(Type context, Class<?> contextRawType, Class<?> containerSupertype) {
    Type containerType = $Gson$Types.getSupertype(context, contextRawType, containerSupertype);

    if (containerType instanceof WildcardType) {
      containerType = ((WildcardType)containerType).getUpperBounds()[0];
    }
    if (containerType instanceof ParameterizedType) {
      return ((ParameterizedType) containerType).getActualTypeArguments()[0];
    }
    return Object.class;
  }
}

(我提交了issue about that in GitHub

您可以通过以下方式使用它:

Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(new StreamTypeAdapterFactory())
        .create();
System.out.println(gson.toJson(Stream.of(1, 2, 3)));