package com.fasterxml.jackson.databind.ser;
import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class TestKeySerializers extends BaseMapTest
{
public static class KarlSerializer extends JsonSerializer<String>
{
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeFieldName("Karl");
}
}
public static class NullKeySerializer extends JsonSerializer<Object>
{
private String _null;
public NullKeySerializer(String s) { _null = s; }
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeFieldName(_null);
}
}
public static class NullValueSerializer extends JsonSerializer<Object>
{
private String _null;
public NullValueSerializer(String s) { _null = s; }
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(_null);
}
}
public static class NotKarlBean
{
public Map<String,Integer> map = new HashMap<String,Integer>();
{
map.put("Not Karl", 1);
}
}
public static class KarlBean
{
@JsonSerialize(keyUsing = KarlSerializer.class)
public Map<String,Integer> map = new HashMap<String,Integer>();
{
map.put("Not Karl", 1);
}
}
enum ABC {
A, B, C
}
enum AbcLC {
A, B, C;
@JsonValue
public String toLC() {
return name().toLowerCase();
}
}
static class ABCKeySerializer extends JsonSerializer<ABC> {
@Override
public void serialize(ABC value, JsonGenerator gen,
SerializerProvider provider) throws IOException {
gen.writeFieldName("xxx"+value);
}
}
@JsonSerialize(keyUsing = ABCKeySerializer.class)
public static enum ABCMixin { }
public static enum Outer {
inner;
}
static class ABCMapWrapper {
public Map<ABC,String> stuff = new HashMap<ABC,String>();
public ABCMapWrapper() {
stuff.put(ABC.B, "bar");
}
}
static class BAR<T>{
T value;
public BAR(T value) {
this.value = value;
}
@JsonValue
public T getValue() {
return value;
}
@Override
public String toString() {
return this.getClass().getSimpleName()
+ ", value:" + value
;
}
}
static class UCString {
private String value;
public UCString(String v) {
value = v.toUpperCase();
}
@JsonValue
public String asString() {
return value;
}
}
/*
/**********************************************************
/* Unit tests
/**********************************************************
*/
private final ObjectMapper MAPPER = new ObjectMapper();
public void testNotKarl() throws IOException {
final String serialized = MAPPER.writeValueAsString(new NotKarlBean());
assertEquals("{\"map\":{\"Not Karl\":1}}", serialized);
}
public void testKarl() throws IOException {
final String serialized = MAPPER.writeValueAsString(new KarlBean());
assertEquals("{\"map\":{\"Karl\":1}}", serialized);
}
// [databind#75]: caching of KeySerializers
public void testBoth() throws IOException
{
// Let's NOT use shared one, to ensure caching starts from clean slate
final ObjectMapper mapper = new ObjectMapper();
final String value1 = mapper.writeValueAsString(new NotKarlBean());
assertEquals("{\"map\":{\"Not Karl\":1}}", value1);
final String value2 = mapper.writeValueAsString(new KarlBean());
assertEquals("{\"map\":{\"Karl\":1}}", value2);
}
// Test custom key serializer for enum
public void testCustomForEnum() throws IOException
{
// can not use shared mapper as we are registering a module
final ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("test");
mod.addKeySerializer(ABC.class, new ABCKeySerializer());
mapper.registerModule(mod);
String json = mapper.writeValueAsString(new ABCMapWrapper());
assertEquals("{\"stuff\":{\"xxxB\":\"bar\"}}", json);
}
public void testCustomNullSerializers() throws IOException
{
final ObjectMapper mapper = new ObjectMapper();
mapper.getSerializerProvider().setNullKeySerializer(new NullKeySerializer("NULL-KEY"));
mapper.getSerializerProvider().setNullValueSerializer(new NullValueSerializer("NULL"));
Map<String,Integer> input = new HashMap<>();
input.put(null, 3);
String json = mapper.writeValueAsString(input);
assertEquals("{\"NULL-KEY\":3}", json);
json = mapper.writeValueAsString(new Object[] { 1, null, true });
assertEquals("[1,\"NULL\",true]", json);
}
public void testCustomEnumInnerMapKey() throws Exception {
Map<Outer, Object> outerMap = new HashMap<Outer, Object>();
Map<ABC, Map<String, String>> map = new EnumMap<ABC, Map<String, String>>(ABC.class);
Map<String, String> innerMap = new HashMap<String, String>();
innerMap.put("one", "1");
map.put(ABC.A, innerMap);
outerMap.put(Outer.inner, map);
final ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("test");
mod.setMixInAnnotation(ABC.class, ABCMixin.class);
mod.addKeySerializer(ABC.class, new ABCKeySerializer());
mapper.registerModule(mod);
JsonNode tree = mapper.convertValue(outerMap, JsonNode.class);
JsonNode innerNode = tree.get("inner");
String key = innerNode.fieldNames().next();
assertEquals("xxxA", key);
}
// [databind#838]
public void testUnWrappedMapWithDefaultType() throws Exception{
final ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("test");
mod.addKeySerializer(ABC.class, new ABCKeySerializer());
mapper.registerModule(mod);
TypeResolverBuilder<?> typer = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
typer = typer.init(JsonTypeInfo.Id.NAME, null);
typer = typer.inclusion(JsonTypeInfo.As.PROPERTY);
//typer = typer.typeProperty(TYPE_FIELD);
typer = typer.typeIdVisibility(true);
mapper.setDefaultTyping(typer);
Map<ABC,String> stuff = new HashMap<ABC,String>();
stuff.put(ABC.B, "bar");
String json = mapper.writerFor(new TypeReference<Map<ABC, String>>() {})
.writeValueAsString(stuff);
assertEquals("{\"@type\":\"HashMap\",\"xxxB\":\"bar\"}", json);
}
// [databind#838]
@SuppressWarnings("deprecation")
public void testUnWrappedMapWithKeySerializer() throws Exception{
SimpleModule mod = new SimpleModule("test");
mod.addKeySerializer(ABC.class, new ABCKeySerializer());
final ObjectMapper mapper = new ObjectMapper()
.registerModule(mod)
.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(SerializationFeature.WRITE_NULL_MAP_VALUES)
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
;
Map<ABC,BAR<?>> stuff = new HashMap<ABC,BAR<?>>();
stuff.put(ABC.B, new BAR<String>("bar"));
String json = mapper.writerFor(new TypeReference<Map<ABC,BAR<?>>>() {})
.writeValueAsString(stuff);
assertEquals("{\"xxxB\":\"bar\"}", json);
}
// [databind#943]
public void testDynamicMapKeys() throws Exception
{
Map<Object,Integer> stuff = new LinkedHashMap<Object,Integer>();
stuff.put(AbcLC.B, Integer.valueOf(3));
stuff.put(new UCString("foo"), Integer.valueOf(4));
String json = MAPPER.writeValueAsString(stuff);
assertEquals(aposToQuotes("{'b':3,'FOO':4}"), json);
}
}