序列化Java 8 Streams

时间:2016-01-12 16:00:16

标签: java json jackson

我想使用Jackson来序列化流的输出。杰克逊没有对序列化java.util.stream.Stream的内在支持,但它能够序列化java.util.Iterator

为了说明这个问题,我想序列化这个简单的界面:

public interface SerializeMe {
    Iterator<Integer> getMyNumbers();
}

我将比较序列化List.iterator()Stream.iterator()的输出结果:

public class SerializeViaList implements SerializeMe {
    @Override
    public Iterator<Integer> getMyNumbers() {
        return Arrays.asList(1, 2, 3).iterator();
    }
}

public class SerializeViaStream implements SerializeMe {
    @Override
    public Iterator<Integer> getMyNumbers() {
        return Arrays.asList(1, 2, 3).stream().iterator();
    }
}

以下方法将演示这两个类的输出:

public static void main(String[] args) throws Exception {
    ObjectMapper mapper = new ObjectMapper();

    System.out.println("via list: " + mapper.writeValueAsString(new SerializeViaList()));
    System.out.println("via stream: " + mapper.writeValueAsString(new SerializeViaStream()));
}

这个输出是:

via list: {"myNumbers":[1,2,3]}
via stream: {"myNumbers":{}}

这表明流迭代器未正确序列化。

有趣的是,如果我添加@JsonSerialize(as=Iterator.class)

会有效
@JsonSerialize(as=Iterator.class)
public Iterator<Integer> getMyNumbers() {
    // ....
}

我不想为流可以创建的每个类型的迭代器编写自定义JsonSerializer。我有什么选择?

1 个答案:

答案 0 :(得分:5)

Iterator只被识别为“附加”接口,即只有在没有bean序列化程序可以为对象构建时才使用它。不幸的是,spliterator适配器确实得到了一个虚拟bean序列化器,因为类有一个注释...这不是很好,甚至看起来都不是预期的(这不是检查员使用的注释) AFAIK)

当您指定@JsonSerialize(as=Iterator.class)时,您将强制解释为Iterator,并且IteratorSerializer工作正常。

这是我之前编写的Jackson模块,它允许通过顺序序列化其内容来序列化Stream(也是LongStream,IntStream或DoubleStream):

public class StreamModule extends SimpleModule {
    public StreamModule() {
        super(StreamModule.class.getSimpleName());
        addSerializer(LongStream.class, new LongStreamSerializer());
        addSerializer(IntStream.class, new IntStreamSerializer());
        addSerializer(DoubleStream.class, new DoubleStreamSerializer());
    }

    @Override
    public void setupModule(SetupContext context) {
        context.addSerializers(new StreamSerializers());
        super.setupModule(context);
    }

    public static class StreamSerializers extends Serializers.Base {
        @Override
        public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) {
            Class<?> raw = type.getRawClass();
            if (Stream.class.isAssignableFrom(raw)) {
                JavaType[] params = config.getTypeFactory().findTypeParameters(type, Stream.class);
                JavaType vt = (params == null || params.length != 1) ? TypeFactory.unknownType() : params[0];
                return new StreamSerializer<Object>(config.getTypeFactory().constructParametrizedType(Stream.class, Stream.class, vt), vt);
            }
            return super.findSerializer(config, type, beanDesc);
        }
    }

    static class StreamSerializer<T> extends StdSerializer<Stream<T>> implements ContextualSerializer {
        private final JavaType streamType;
        private final JavaType elemType;

        public StreamSerializer(JavaType streamType, JavaType elemType) {
            super(streamType);
            this.streamType = streamType;
            this.elemType = elemType;
        }

        @Override
        public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property) throws JsonMappingException {
            if (!elemType.hasRawClass(Object.class) && (provider.isEnabled(MapperFeature.USE_STATIC_TYPING) || elemType.isFinal())) {
                JsonSerializer<Object> elemSerializer = provider.findPrimaryPropertySerializer(elemType, property);
                return new TypedStreamSerializer<T>(streamType, elemSerializer);
            }
            return this;
        }

        @Override
        public void serialize(Stream<T> stream, JsonGenerator jgen, SerializerProvider provider) throws IOException,
                JsonGenerationException {
            jgen.writeStartArray();
            try {
                stream.forEachOrdered(elem -> {
                    try {
                        provider.defaultSerializeValue(elem, jgen);
                    } catch (IOException e) {
                        throw new WrappedIOException(e);
                    }
                });
            } catch (WrappedIOException e) {
                throw (IOException) e.getCause();
            }
            jgen.writeEndArray();
        }
    }

    static class TypedStreamSerializer<T> extends StdSerializer<Stream<T>> {
        private final JsonSerializer<T> elemSerializer;

        @SuppressWarnings("unchecked")
        public TypedStreamSerializer(JavaType streamType, JsonSerializer<?> elemSerializer) {
            super(streamType);
            this.elemSerializer = (JsonSerializer<T>) elemSerializer;
        }

        @Override
        public void serialize(Stream<T> stream, JsonGenerator jgen, SerializerProvider provider) throws IOException,
                JsonGenerationException {
            jgen.writeStartArray();
            try {
                stream.forEachOrdered(elem -> {
                    try {
                        elemSerializer.serialize(elem, jgen, provider);
                    } catch (IOException e) {
                        throw new WrappedIOException(e);
                    }
                });
            } catch (WrappedIOException e) {
                throw (IOException) e.getCause();
            }
            jgen.writeEndArray();
        }
    }

    static class IntStreamSerializer extends StdSerializer<IntStream> {
        public IntStreamSerializer() {
            super(IntStream.class);
        }

        @Override
        public void serialize(IntStream stream, JsonGenerator jgen, SerializerProvider provider) throws IOException,
                JsonGenerationException {
            jgen.writeStartArray();
            try {
                stream.forEachOrdered(value -> {
                    try {
                        jgen.writeNumber(value);
                    } catch (IOException e) {
                        throw new WrappedIOException(e);
                    }
                });
            } catch (WrappedIOException e) {
                throw (IOException) e.getCause();
            }
            jgen.writeEndArray();
        }
    }

    static class LongStreamSerializer extends StdSerializer<LongStream> {
        public LongStreamSerializer() {
            super(LongStream.class);
        }

        @Override
        public void serialize(LongStream stream, JsonGenerator jgen, SerializerProvider provider) throws IOException,
                JsonGenerationException {
            jgen.writeStartArray();
            try {
                stream.forEachOrdered(value -> {
                    try {
                        jgen.writeNumber(value);
                    } catch (IOException e) {
                        throw new WrappedIOException(e);
                    }
                });
            } catch (WrappedIOException e) {
                throw (IOException) e.getCause();
            }
            jgen.writeEndArray();
        }
    }

    static class DoubleStreamSerializer extends StdSerializer<DoubleStream> {
        public DoubleStreamSerializer() {
            super(DoubleStream.class);
        }

        @Override
        public void serialize(DoubleStream stream, JsonGenerator jgen, SerializerProvider provider) throws IOException,
                JsonGenerationException {
            jgen.writeStartArray();
            try {
                stream.forEachOrdered(value -> {
                    try {
                        jgen.writeNumber(value);
                    } catch (IOException e) {
                        throw new WrappedIOException(e);
                    }
                });
            } catch (WrappedIOException e) {
                throw (IOException) e.getCause();
            }
            jgen.writeEndArray();
        }
    }

    public static final class WrappedIOException extends RuntimeException {
        private WrappedIOException(IOException e) {
            super(e);
        }
    }
}