package com.fasterxml.jackson.databind.seq; import java.io.Closeable; import java.io.IOException; import java.io.StringWriter; import java.util.*; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.io.SerializedString; import com.fasterxml.jackson.databind.*; public class SequenceWriterTest extends BaseMapTest { static class Bean { public int a; public Bean(int value) { a = value; } @Override public boolean equals(Object o) { if (o == null || o.getClass() != getClass()) return false; Bean other = (Bean) o; return other.a == this.a; } @Override public int hashCode() { return a; } } @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") static class PolyBase { } @JsonTypeName("A") static class ImplA extends PolyBase { public int value; public ImplA(int v) { value = v; } } @JsonTypeName("B") static class ImplB extends PolyBase { public int b; public ImplB(int v) { b = v; } } static class BareBase { public int a = 1; } @JsonPropertyOrder({ "a", "b" }) static class BareBaseExt extends BareBase { public int b = 2; } static class BareBaseCloseable extends BareBase implements Closeable { public int c = 3; boolean closed = false; @Override public void close() throws IOException { closed = true; } } static class CloseableValue implements Closeable { public int x; public boolean closed; @Override public void close() throws IOException { closed = true; } } /* /********************************************************** /* Test methods, simple writes /********************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); private final ObjectWriter WRITER = MAPPER.writer() .withRootValueSeparator("\n"); public void testSimpleNonArray() throws Exception { StringWriter strw = new StringWriter(); SequenceWriter w = WRITER .forType(Bean.class) .writeValues(strw); w.write(new Bean(13)) .write(new Bean(-6)) .writeAll(new Bean[] { new Bean(3), new Bean(1) }) .writeAll(Arrays.asList(new Bean(5), new Bean(7))) ; w.close(); assertEquals(aposToQuotes("{'a':13}\n{'a':-6}\n{'a':3}\n{'a':1}\n{'a':5}\n{'a':7}"), strw.toString()); strw = new StringWriter(); JsonGenerator gen = WRITER.getFactory().createGenerator(strw); w = WRITER .withRootValueSeparator(new SerializedString("/")) .writeValues(gen); w.write(new Bean(1)) .write(new Bean(2)); w.close(); gen.close(); assertEquals(aposToQuotes("{'a':1}/{'a':2}"), strw.toString()); } public void testSimpleArray() throws Exception { StringWriter strw = new StringWriter(); SequenceWriter w = WRITER.writeValuesAsArray(strw); w.write(new Bean(1)) .write(new Bean(2)) .writeAll(new Bean[] { new Bean(-7), new Bean(2) }); w.close(); assertEquals(aposToQuotes("[{'a':1},{'a':2},{'a':-7},{'a':2}]"), strw.toString()); strw = new StringWriter(); JsonGenerator gen = WRITER.getFactory().createGenerator(strw); w = WRITER.writeValuesAsArray(gen); Collection<Bean> bean = Collections.singleton(new Bean(3)); w.write(new Bean(1)) .write(null) .writeAll((Iterable<Bean>) bean); w.close(); gen.close(); assertEquals(aposToQuotes("[{'a':1},null,{'a':3}]"), strw.toString()); } /* /********************************************************** /* Test methods, polymorphic writes /********************************************************** */ @SuppressWarnings("resource") public void testPolymorphicNonArrayWithoutType() throws Exception { StringWriter strw = new StringWriter(); SequenceWriter w = WRITER .writeValues(strw); w.write(new ImplA(3)) .write(new ImplA(4)) .close(); assertEquals(aposToQuotes("{'type':'A','value':3}\n{'type':'A','value':4}"), strw.toString()); } @SuppressWarnings("resource") public void testPolymorphicArrayWithoutType() throws Exception { StringWriter strw = new StringWriter(); SequenceWriter w = WRITER .writeValuesAsArray(strw); w.write(new ImplA(-1)) .write(new ImplA(6)) .close(); assertEquals(aposToQuotes("[{'type':'A','value':-1},{'type':'A','value':6}]"), strw.toString()); } public void testPolymorphicArrayWithType() throws Exception { StringWriter strw = new StringWriter(); SequenceWriter w = WRITER .forType(PolyBase.class) .writeValuesAsArray(strw); w.write(new ImplA(-1)) .write(new ImplB(3)) .write(new ImplA(7)); w.flush(); w.close(); assertEquals(aposToQuotes("[{'type':'A','value':-1},{'type':'B','b':3},{'type':'A','value':7}]"), strw.toString()); } @SuppressWarnings("resource") public void testSimpleCloseable() throws Exception { ObjectWriter w = MAPPER.writer() .with(SerializationFeature.CLOSE_CLOSEABLE); CloseableValue input = new CloseableValue(); assertFalse(input.closed); StringWriter out = new StringWriter(); SequenceWriter seq = w.writeValues(out); input = new CloseableValue(); assertFalse(input.closed); seq.write(input); assertTrue(input.closed); seq.close(); input.close(); assertEquals(aposToQuotes("{'x':0,'closed':false}"), out.toString()); } public void testWithExplicitType() throws Exception { ObjectWriter w = MAPPER.writer() // just for fun (and higher coverage): .without(SerializationFeature.FLUSH_AFTER_WRITE_VALUE) .with(SerializationFeature.CLOSE_CLOSEABLE); StringWriter out = new StringWriter(); SequenceWriter seq = w.writeValues(out); // first full, as-is seq.write(new BareBaseExt()); // but then just base type (no 'b' field) seq.write(new BareBaseExt(), MAPPER.constructType(BareBase.class)); // one more. And note! Check for Closeable is for _value_, not type // so it's fine to expect closing here BareBaseCloseable cl = new BareBaseCloseable(); seq.write(cl, MAPPER.constructType(BareBase.class)); assertTrue(cl.closed); cl.close(); seq.close(); seq.flush(); assertEquals(aposToQuotes("{'a':1,'b':2} {'a':1} {'a':1}"), out.toString()); } }