我想使用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
。我有什么选择?
答案 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);
}
}
}