JDKTypeSerializationTest.java
package tools.jackson.databind.ser.jdk;
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.regex.Pattern;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.*;
import tools.jackson.core.*;
import tools.jackson.core.json.JsonWriteFeature;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.*;
import tools.jackson.databind.annotation.JsonSerialize;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.ser.std.StdScalarSerializer;
import tools.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit tests for JDK types not covered by other tests (i.e. things
* that are not Enums, Collections, Maps, or standard Date/Time types)
*/
public class JDKTypeSerializationTest
extends DatabindTestUtil
{
// // // Inner types from JDKTypeSerializationTest
static final class AppId implements CharSequence {
private final long value;
public AppId(long value) throws IllegalArgumentException {
this.value = value;
}
public static AppId valueOf(String value) throws IllegalArgumentException {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
return new AppId(Long.parseLong(value));
}
@Override
public int length() {
return toString().length();
}
@Override
public char charAt(int index) {
return toString().charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return toString().subSequence(start, end);
}
// pay attention: no @JsonValue here
@Override
public String toString() {
return Long.toString(value);
}
}
static class InetAddressBean {
public InetAddress value;
public InetAddressBean(InetAddress i) { value = i; }
}
// [databind#2197]
static class VoidBean {
public Void value;
}
// // // Inner types from CustomExceptionSer5194Test
static class MyIllegalArgumentException extends RuntimeException {
private static final long serialVersionUID = 1L;
public MyIllegalArgumentException() {
super();
}
public MyIllegalArgumentException(String s) {
super(s);
}
public MyIllegalArgumentException(String message, Throwable cause) {
super(message, cause);
}
public MyIllegalArgumentException(Throwable cause) {
super(cause);
}
}
// // // Inner types from UUIDSerializationTest
private final static String nullUUIDStr = "00000000-0000-0000-0000-000000000000";
private final static UUID nullUUID = UUID.fromString(nullUUIDStr);
static class UUIDWrapperVanilla {
public UUID uuid;
public UUIDWrapperVanilla(UUID u) { uuid = u; }
}
static class UUIDWrapperBinary {
// default with JSON is String, for use of (base64-encoded) Binary:
@JsonFormat(shape = JsonFormat.Shape.BINARY)
public UUID uuid;
public UUIDWrapperBinary(UUID u) { uuid = u; }
}
// // // Inner types from AtomicTypeSerializationTest
static class UpperCasingSerializer extends StdScalarSerializer<String>
{
public UpperCasingSerializer() { super(String.class); }
@Override
public void serialize(String value, JsonGenerator gen,
SerializationContext provider) {
gen.writeString(value.toUpperCase());
}
}
static class UCStringWrapper {
@JsonSerialize(contentUsing=UpperCasingSerializer.class)
public AtomicReference<String> value;
public UCStringWrapper(String s) { value = new AtomicReference<String>(s); }
}
// [datatypes-java8#17]
@JsonPropertyOrder({ "date1", "date2", "date" })
static class ContextualOptionals
{
public AtomicReference<Date> date;
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy+MM+dd")
public AtomicReference<Date> date1;
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy*MM*dd")
public AtomicReference<Date> date2;
}
// [databind#1673]
static class ContainerA {
public AtomicReference<Strategy> strategy =
new AtomicReference<>((Strategy) new Foo(42));
}
static class ContainerB {
public AtomicReference<List<Strategy>> strategy;
{
List<Strategy> list = new ArrayList<>();
list.add(new Foo(42));
strategy = new AtomicReference<>(list);
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({ @JsonSubTypes.Type(name = "Foo", value = Foo.class) })
interface Strategy { }
static class Foo implements Strategy {
public int foo;
@JsonCreator
Foo(@JsonProperty("foo") int foo) {
this.foo = foo;
}
}
// [databind#2565]: problems with JsonUnwrapped, non-unwrappable type
static class MyBean2565 {
@JsonUnwrapped
public AtomicReference<String> maybeText = new AtomicReference<>("value");
}
/*
/**********************************************************************
/* Test methods
/**********************************************************************
*/
private final ObjectMapper MAPPER = sharedMapper();
private final ObjectWriter WRITER = MAPPER.writer();
// // // Tests from JDKTypeSerializationTest
@Test
public void testFile() throws IOException
{
// this may get translated to different representation on Windows, maybe Mac:
File f = new File(new File("/tmp"), "foo.text");
String str = MAPPER.writer()
.without(JsonWriteFeature.ESCAPE_FORWARD_SLASHES)
.writeValueAsString(f);
// escape backslashes (for portability with windows)
String escapedAbsPath = f.getAbsolutePath().replaceAll("\\\\", "\\\\\\\\");
assertEquals(q(escapedAbsPath), str);
}
@Test
public void testRegexps() throws IOException
{
final String PATTERN_STR = "\\s+([a-b]+)\\w?";
Pattern p = Pattern.compile(PATTERN_STR);
Map<String,Object> input = new HashMap<String,Object>();
input.put("p", p);
Map<String,Object> result = writeAndMap(MAPPER, input);
assertEquals(p.pattern(), result.get("p"));
}
@Test
public void testCurrency() throws IOException
{
Currency usd = Currency.getInstance("USD");
assertEquals(q("USD"), MAPPER.writeValueAsString(usd));
}
@SuppressWarnings("deprecation")
@Test
public void testLocale() throws IOException
{
assertEquals(q("en"), MAPPER.writeValueAsString(new Locale("en")));
assertEquals(q("es-ES"), MAPPER.writeValueAsString(new Locale("es", "ES")));
// 15-Feb-2017, tatu: wrt [databind#1600], can only assume this is expected...
assertEquals(q("fi-FI-x-lvariant-savo"), MAPPER.writeValueAsString(new Locale("FI", "fi", "savo")));
assertEquals(q("en-US"), MAPPER.writeValueAsString(Locale.US));
// [databind#1123]
assertEquals(q(""), MAPPER.writeValueAsString(Locale.ROOT));
}
@Test
public void testInetAddress() throws IOException
{
assertEquals(q("127.0.0.1"), MAPPER.writeValueAsString(InetAddress.getByName("127.0.0.1")));
InetAddress input = InetAddress.getByName("google.com");
assertEquals(q("google.com"), MAPPER.writeValueAsString(input));
ObjectMapper mapper = jsonMapperBuilder()
.withConfigOverride(InetAddress.class,
o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER)))
.build();
String json = mapper.writeValueAsString(input);
assertEquals(q(input.getHostAddress()), json);
assertEquals(String.format("{\"value\":\"%s\"}", input.getHostAddress()),
mapper.writeValueAsString(new InetAddressBean(input)));
}
@Test
public void testInetSocketAddress() throws IOException
{
assertEquals(q("127.0.0.1:8080"),
MAPPER.writeValueAsString(new InetSocketAddress("127.0.0.1", 8080)));
assertEquals(q("google.com:6667"),
MAPPER.writeValueAsString(new InetSocketAddress("google.com", 6667)));
assertEquals(q("[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443"),
MAPPER.writeValueAsString(new InetSocketAddress("2001:db8:85a3:8d3:1319:8a2e:370:7348", 443)));
}
// [JACKSON-597]
@Test
public void testClass() throws IOException
{
assertEquals(q("java.lang.String"), MAPPER.writeValueAsString(String.class));
assertEquals(q("int"), MAPPER.writeValueAsString(Integer.TYPE));
assertEquals(q("boolean"), MAPPER.writeValueAsString(Boolean.TYPE));
assertEquals(q("void"), MAPPER.writeValueAsString(Void.TYPE));
}
@Test
public void testCharset() throws IOException
{
assertEquals(q("UTF-8"), MAPPER.writeValueAsString(Charset.forName("UTF-8")));
}
// NOTE: Does NOT really belong here as it is about serialization...
// but there's no proper place yet.
// [databind#3305]
@Test
public void testCharSequenceSerialization() throws Exception
{
final String APP_ID = "3074457345618296002";
AppId appId = AppId.valueOf(APP_ID);
String serialized = MAPPER.writeValueAsString(appId);
//Without a fix fails on JDK17 with
// ComparisonFailure:
//Expected :{"empty":false}
//Actual :"3074457345618296002"
assertEquals("\"" + APP_ID + "\"", serialized);
}
// [databind#239]: Support serialization of ByteBuffer
@Test
public void testByteBuffer() throws IOException
{
final byte[] INPUT_BYTES = new byte[] { 1, 2, 3, 4, 5 };
String exp = MAPPER.writeValueAsString(INPUT_BYTES);
ByteBuffer bbuf = ByteBuffer.wrap(INPUT_BYTES);
assertEquals(exp, MAPPER.writeValueAsString(bbuf));
// so far so good, but must ensure Native buffers also work:
ByteBuffer bbuf2 = ByteBuffer.allocateDirect(5);
bbuf2.put(INPUT_BYTES);
bbuf2.flip();
assertEquals(exp, MAPPER.writeValueAsString(bbuf2));
}
// [databind#1662]: Sliced ByteBuffers
@Test
public void testSlicedByteBuffer() throws IOException
{
final byte[] INPUT_BYTES = new byte[] { 1, 2, 3, 4, 5 };
ByteBuffer bbuf = ByteBuffer.wrap(INPUT_BYTES);
bbuf.position(2);
ByteBuffer slicedBuf = bbuf.slice();
assertEquals(MAPPER.writeValueAsString(new byte[] { 3, 4, 5 }),
MAPPER.writeValueAsString(slicedBuf));
// but how about offset within?
slicedBuf.position(1);
assertEquals(MAPPER.writeValueAsString(new byte[] { 4, 5 }),
MAPPER.writeValueAsString(slicedBuf));
}
// [databind#2602]: Need to consider position()
@Test
public void testDuplicatedByteBufferWithCustomPosition() throws IOException
{
final byte[] INPUT_BYTES = new byte[] { 1, 2, 3, 4, 5 };
String exp = MAPPER.writeValueAsString(new byte[] { 3, 4, 5 });
ByteBuffer bbuf = ByteBuffer.wrap(INPUT_BYTES);
bbuf.position(2);
ByteBuffer duplicated = bbuf.duplicate();
assertEquals(exp, MAPPER.writeValueAsString(duplicated));
// also check differently constructed bytebuffer (noting that
// offset given is the _position_ to use, NOT array offset
exp = MAPPER.writeValueAsString(new byte[] { 2, 3, 4 });
bbuf = ByteBuffer.wrap(INPUT_BYTES, 1, 3);
assertEquals(exp, MAPPER.writeValueAsString(bbuf.duplicate()));
}
// [databind#4164]: No rewinding for direct buffer
@Test
public void testDuplicatedByteBufferWithCustomPositionDirect() throws IOException
{
final byte[] INPUT_BYTES = new byte[] { 1, 2, 3, 4, 5 };
String exp = MAPPER.writeValueAsString(new byte[] { 3, 4, 5 });
ByteBuffer bbuf = ByteBuffer.allocateDirect(INPUT_BYTES.length);
bbuf.put(INPUT_BYTES);
bbuf.position(2);
ByteBuffer duplicated = bbuf.duplicate();
assertEquals(exp, MAPPER.writeValueAsString(duplicated));
}
// [databind#2197]
@Test
public void testVoidSerialization() throws Exception
{
assertEquals(a2q("{'value':null}"),
MAPPER.writeValueAsString(new VoidBean()));
}
// [databind#2657]
@Test
public void testNonStandardProperties() throws Exception
{
Properties properties = new Properties();
// Bad usage: Properties should NOT contain non-Strings. But
// some do that regardless and compiler won't stop it so.
properties.put("key", 1);
String json = MAPPER.writeValueAsString(properties);
assertEquals("{\"key\":1}", json);
}
// [databind#3130]: fails on JDK 11+
@Test
public void testThreadSerialization() throws Exception
{
final Thread input = Thread.currentThread();
Map<?,?> asMap = MAPPER.convertValue(input, Map.class);
// Should get empty "contextClassLoader"
Map<?,?> cl = (Map<?,?>) asMap.get("contextClassLoader");
assertNotNull(cl);
assertEquals(0, cl.size());
}
// [databind#3522]: ByteArrayOutputStream
@Test
public void testByteArrayOutputStreamSerialization() throws Exception
{
byte[] bytes = new byte[] { 1, 11, 111 };
final String exp = MAPPER.writeValueAsString(bytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(bytes);
assertEquals(exp, MAPPER.writeValueAsString(baos));
}
// // // Tests from UntypedSerializationTest
@Test
public void testFromArray() throws Exception
{
ArrayList<Object> doc = new ArrayList<Object>();
doc.add("Elem1");
doc.add(Integer.valueOf(3));
Map<String,Object> struct = new LinkedHashMap<String, Object>();
struct.put("first", Boolean.TRUE);
struct.put("Second", new ArrayList<Object>());
doc.add(struct);
doc.add(Boolean.FALSE);
// loop more than once, just to ensure caching works ok (during second round)
for (int i = 0; i < 3; ++i) {
String str = MAPPER.writeValueAsString(doc);
try (JsonParser p = MAPPER.createParser(str)) {
assertEquals(JsonToken.START_ARRAY, p.nextToken());
assertEquals(JsonToken.VALUE_STRING, p.nextToken());
assertEquals("Elem1", getAndVerifyText(p));
assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(3, p.getIntValue());
assertEquals(JsonToken.START_OBJECT, p.nextToken());
assertEquals(JsonToken.PROPERTY_NAME, p.nextToken());
assertEquals("first", getAndVerifyText(p));
assertEquals(JsonToken.VALUE_TRUE, p.nextToken());
assertEquals(JsonToken.PROPERTY_NAME, p.nextToken());
assertEquals("Second", getAndVerifyText(p));
if (p.nextToken() != JsonToken.START_ARRAY) {
fail("Expected START_ARRAY: JSON == '"+str+"'");
}
assertEquals(JsonToken.END_ARRAY, p.nextToken());
assertEquals(JsonToken.END_OBJECT, p.nextToken());
assertEquals(JsonToken.VALUE_FALSE, p.nextToken());
assertEquals(JsonToken.END_ARRAY, p.nextToken());
assertNull(p.nextToken());
}
}
}
@Test
public void testFromMap() throws Exception
{
LinkedHashMap<String,Object> doc = new LinkedHashMap<String,Object>();
doc.put("a1", "\"text\"");
doc.put("int", Integer.valueOf(137));
doc.put("foo bar", Long.valueOf(1234567890L));
for (int i = 0; i < 3; ++i) {
String str = MAPPER.writeValueAsString(doc);
try (JsonParser p = MAPPER.createParser(str)) {
assertEquals(JsonToken.START_OBJECT, p.nextToken());
assertEquals(JsonToken.PROPERTY_NAME, p.nextToken());
assertEquals("a1", getAndVerifyText(p));
assertEquals(JsonToken.VALUE_STRING, p.nextToken());
assertEquals("\"text\"", getAndVerifyText(p));
assertEquals(JsonToken.PROPERTY_NAME, p.nextToken());
assertEquals("int", getAndVerifyText(p));
assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(137, p.getIntValue());
assertEquals(JsonToken.PROPERTY_NAME, p.nextToken());
assertEquals("foo bar", getAndVerifyText(p));
assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(1234567890L, p.getLongValue());
assertEquals(JsonToken.END_OBJECT, p.nextToken());
assertNull(p.nextToken());
}
}
}
// // // Tests from CustomExceptionSer5194Test
// [databind#5194]: failed to serialize custom exception
// 09-Jul-2025, tatu: Works for 2.x, fails for 3.x -- no idea why, disabled for now
@Disabled
@Test
public void test5194() throws Exception {
ObjectMapper mapper = JsonMapper.builder()
.changeDefaultVisibility(vc -> vc
.withVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
.withVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
)
.build();
String json = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(new MyIllegalArgumentException());
assertNotNull(json);
}
// // // Tests from UUIDSerializationTest
// Verify that efficient UUID codec won't mess things up:
@Test
public void testBasicUUIDs() throws Exception
{
// first, couple of generated UUIDs:
for (String value : new String[] {
"76e6d183-5f68-4afa-b94a-922c1fdb83f8",
"540a88d1-e2d8-4fb1-9396-9212280d0a7f",
"2c9e441d-1cd0-472d-9bab-69838f877574",
"591b2869-146e-41d7-8048-e8131f1fdec5",
"82994ac2-7b23-49f2-8cc5-e24cf6ed77be",
"00000007-0000-0000-0000-000000000000"
}) {
UUID uuid = UUID.fromString(value);
String json = MAPPER.writeValueAsString(uuid);
assertEquals(q(uuid.toString()), json);
// Also, wrt [#362], should convert cleanly
String str = MAPPER.convertValue(uuid, String.class);
assertEquals(value, str);
}
// then use templating; note that these are not exactly valid UUIDs
// wrt spec (type bits etc), but JDK UUID should deal ok
final String TEMPL = "00000000-0000-0000-0000-000000000000";
final String chars = "123456789abcdef";
for (int i = 0; i < chars.length(); ++i) {
String value = TEMPL.replace('0', chars.charAt(i));
UUID uuid = UUID.fromString(value);
String json = MAPPER.writeValueAsString(uuid);
assertEquals(q(uuid.toString()), json);
}
}
@Test
public void testShapeOverrides() throws Exception
{
// First, see that Binary per-property override works:
assertEquals("{\"uuid\":\"AAAAAAAAAAAAAAAAAAAAAA==\"}",
MAPPER.writeValueAsString(new UUIDWrapperBinary(nullUUID)));
// but that without one we'd get String
assertEquals("{\"uuid\":\""+nullUUIDStr+"\"}",
MAPPER.writeValueAsString(new UUIDWrapperVanilla(nullUUID)));
// but can also override by type
ObjectMapper m = JsonMapper.builder()
.withConfigOverride(UUID.class,
cfg -> cfg.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.BINARY))
)
.build();
assertEquals("{\"uuid\":\"AAAAAAAAAAAAAAAAAAAAAA==\"}",
m.writeValueAsString(new UUIDWrapperVanilla(nullUUID)));
}
// [databind#5225]: problem with tree conversion
@Test
public void testTreeConversion() throws Exception
{
// First, reported issue
JsonNode node = MAPPER.valueToTree(nullUUID);
assertEquals(nullUUIDStr, node.asString());
// and then a variations
Object ob = MAPPER.convertValue(nullUUID, Object.class);
assertEquals(String.class, ob.getClass());
}
// [databind#5323]: problem via JsonGenerator
@Test
public void testSerialization5323Mapper1() throws Exception
{
StringWriter sw = new StringWriter();
_write5323(MAPPER.createGenerator(sw));
_assert5323(sw.toString());
}
@Test
public void testSerialization5323Mapper2() throws Exception
{
ByteArrayOutputStream b = new ByteArrayOutputStream();
_write5323(MAPPER.createGenerator(b));
_assert5323(b.toString(StandardCharsets.UTF_8));
}
@Test
public void testSerialization5323Mapper2b() throws Exception
{
ByteArrayOutputStream b = new ByteArrayOutputStream();
_write5323(MAPPER.createGenerator(b, JsonEncoding.UTF8));
_assert5323(b.toString(StandardCharsets.UTF_8));
}
@Test
public void testSerialization5323ObjectWriter1() throws Exception
{
StringWriter sw = new StringWriter();
_write5323(WRITER.createGenerator(sw));
_assert5323(sw.toString());
}
@Test
public void testSerialization5323ObjectWriter2() throws Exception
{
ByteArrayOutputStream b = new ByteArrayOutputStream();
_write5323(WRITER.createGenerator(b));
_assert5323(b.toString(StandardCharsets.UTF_8));
}
@Test
public void testSerialization5323ObjectWriter2b() throws Exception
{
ByteArrayOutputStream b = new ByteArrayOutputStream();
_write5323(WRITER.createGenerator(b, JsonEncoding.UTF8));
_assert5323(b.toString(StandardCharsets.UTF_8));
}
// // // Tests from AtomicTypeSerializationTest
@Test
public void testAtomicBoolean() throws Exception
{
assertEquals("true", MAPPER.writeValueAsString(new AtomicBoolean(true)));
assertEquals("false", MAPPER.writeValueAsString(new AtomicBoolean(false)));
}
@Test
public void testAtomicInteger() throws Exception
{
assertEquals("1", MAPPER.writeValueAsString(new AtomicInteger(1)));
assertEquals("-9", MAPPER.writeValueAsString(new AtomicInteger(-9)));
}
@Test
public void testAtomicLong() throws Exception
{
assertEquals("0", MAPPER.writeValueAsString(new AtomicLong(0)));
}
@Test
public void testAtomicReference() throws Exception
{
String[] strs = new String[] { "abc" };
assertEquals("[\"abc\"]", MAPPER.writeValueAsString(new AtomicReference<String[]>(strs)));
}
@Test
public void testCustomSerializer() throws Exception
{
final String VALUE = "fooBAR";
String json = MAPPER.writeValueAsString(new UCStringWrapper(VALUE));
assertEquals(json, a2q("{'value':'FOOBAR'}"));
}
@Test
public void testContextualAtomicReference() throws Exception
{
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
ObjectMapper mapper = jsonMapperBuilder()
.disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES)
.defaultDateFormat(df)
.build();
ContextualOptionals input = new ContextualOptionals();
input.date = new AtomicReference<>(new Date(0L));
input.date1 = new AtomicReference<>(new Date(0L));
input.date2 = new AtomicReference<>(new Date(0L));
final String json = mapper.writeValueAsString(input);
assertEquals(a2q(
"{'date1':'1970+01+01','date2':'1970*01*01','date':'1970/01/01'}"),
json);
}
// [databind#1673]
@Test
public void testPolymorphicReferenceSimple() throws Exception
{
final String EXPECTED = "{\"type\":\"Foo\",\"foo\":42}";
String json = MAPPER.writeValueAsString(new ContainerA());
assertEquals("{\"strategy\":" + EXPECTED + "}", json);
}
// [databind#1673]
@Test
public void testPolymorphicReferenceListOf() throws Exception
{
final String EXPECTED = "{\"type\":\"Foo\",\"foo\":42}";
// Reproduction of issue seen with scala.Option and java8 Optional types:
// https://github.com/FasterXML/jackson-module-scala/issues/346#issuecomment-336483326
String json = MAPPER.writeValueAsString(new ContainerB());
assertEquals("{\"strategy\":[" + EXPECTED + "]}", json);
}
// [databind#2565]: problems with JsonUnwrapped, non-unwrappable type
@Test
public void testWithUnwrappableUnwrapped() throws Exception
{
assertEquals(a2q("{'maybeText':'value'}"),
MAPPER.writeValueAsString(new MyBean2565()));
}
// [databind#5616]: AtomicReference with subtype, serialization as supertype
@Test
public void testAtomicReferenceWithSubtypeProperties() throws Exception
{
String json = MAPPER.writerFor(new TypeReference<AtomicReference<Strategy>>() {})
.writeValueAsString(new AtomicReference<>(new Foo(99)));
// Must include subtype property "foo", not just type info
assertEquals("{\"type\":\"Foo\",\"foo\":99}", json);
}
// // // Private helpers from UUIDSerializationTest
private void _write5323(JsonGenerator g) {
g.writeStartObject();
g.writePOJOProperty("id", nullUUID);
g.writeEndObject();
g.close();
}
private void _assert5323(String json) {
assertEquals("{\"id\":\""+nullUUIDStr+"\"}", json);
}
}