Compare commits

...

9 Commits

Author SHA1 Message Date
wenshao
f8186d8378 simplify 2025-02-20 07:28:53 +08:00
wenshao
d772d2cd9f bug fix 2025-02-20 07:26:47 +08:00
wenshao
20b47d44b8 contentAs support Map 2025-02-20 07:16:05 +08:00
wenshao
6b68529a7f bug fix 2025-02-20 03:01:59 +08:00
wenshao
25006c75e6 fix checkstyle 2025-02-20 02:56:53 +08:00
wenshao
a6cdd59d06 bug fix 2025-02-20 02:50:11 +08:00
wenshao
b7cc4438ca fix locale is null 2025-02-20 02:36:36 +08:00
wenshao
fce3b4c445 fix locale is null 2025-02-20 02:33:01 +08:00
wenshao
57fa6b5e77 support JSONField#contentAs 2025-02-19 09:05:23 +08:00
19 changed files with 633 additions and 27 deletions

View File

@ -121,4 +121,9 @@ public @interface JSONField {
* @since 2.0.52
*/
Class<?> arrayToMapDuplicateHandler() default Void.class;
/**
* @since 2.0.56
*/
Class<?> contentAs() default Void.class;
}

View File

@ -22,6 +22,7 @@ public class FieldInfo {
public static final long DISABLE_JSONB = 1L << 60;
public static final long BACKR_EFERENCE = 1L << 61;
public static final long RECORD = 1L << 62;
public static final long CONTENT_AS = 1L << 63;
public String fieldName;
public String format;
@ -46,6 +47,11 @@ public class FieldInfo {
public String arrayToMapKey;
public Class<?> arrayToMapDuplicateHandler;
/**
* @since 2.0.56
*/
public Class<?> contentAs;
public ObjectReader getInitReader() {
Class<?> calzz = readUsing;
if (calzz != null && ObjectReader.class.isAssignableFrom(calzz)) {
@ -97,5 +103,6 @@ public class FieldInfo {
arrayToMapKey = null;
arrayToMapDuplicateHandler = null;
contentAs = null;
}
}

View File

@ -1308,10 +1308,16 @@ public class ObjectReaderBaseModule
if (!keyName.isEmpty()) {
fieldInfo.arrayToMapKey = keyName;
}
Class<?> arrayToMapDuplicateHandler = jsonField.arrayToMapDuplicateHandler();
if (arrayToMapDuplicateHandler != Void.class) {
fieldInfo.arrayToMapDuplicateHandler = arrayToMapDuplicateHandler;
}
Class<?> contentAs = jsonField.contentAs();
if (contentAs != Void.class) {
fieldInfo.contentAs = contentAs;
}
}
}

View File

@ -24,6 +24,7 @@ abstract class FieldWriterList<T>
ObjectWriter listWriter;
ObjectWriter itemObjectWriter;
final boolean writeAsString;
final Class<?> contentAs;
FieldWriterList(
String name,
@ -35,9 +36,11 @@ abstract class FieldWriterList<T>
Type fieldType,
Class fieldClass,
Field field,
Method method
Method method,
Class<?> contentAs
) {
super(name, ordinal, features, format, null, label, fieldType, fieldClass, field, method);
this.contentAs = contentAs;
writeAsString = (features & WriteNonStringValueAsString.mask) != 0;
@ -77,6 +80,13 @@ abstract class FieldWriterList<T>
@Override
public ObjectWriter getItemWriter(JSONWriter jsonWriter, Type itemType) {
if (contentAs != null) {
ObjectWriter itemObjectWriter = this.itemObjectWriter;
if (itemObjectWriter != null) {
return itemObjectWriter;
}
return this.itemObjectWriter = jsonWriter.getObjectWriter(this.contentAs, contentAs);
}
if (itemType == null || itemType == this.itemType) {
if (itemObjectWriter != null) {
return itemObjectWriter;

View File

@ -19,9 +19,10 @@ final class FieldWriterListField<T>
String label,
Type fieldType,
Class fieldClass,
Field field
Field field,
Class<?> contentAs
) {
super(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field, null);
super(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field, null, contentAs);
}
@Override

View File

@ -25,9 +25,10 @@ final class FieldWriterListFunc<T>
Method method,
Function<T, List> function,
Type fieldType,
Class fieldClass
Class fieldClass,
Class<?> contentAs
) {
super(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field, method);
super(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field, method, contentAs);
this.function = function;
}

View File

@ -23,9 +23,10 @@ final class FieldWriterListMethod<T>
Field field,
Method method,
Type fieldType,
Class fieldClass
Class fieldClass,
Class<?> contentAs
) {
super(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field, method);
super(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field, method, contentAs);
}
@Override

View File

@ -0,0 +1,88 @@
package com.alibaba.fastjson2.writer;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.codec.FieldInfo;
import com.alibaba.fastjson2.util.ParameterizedTypeImpl;
import com.alibaba.fastjson2.util.TypeUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Locale;
abstract class FieldWriterMap
extends FieldWriterObject {
protected final Class<?> contentAs;
protected Type contentAsFieldType;
volatile ObjectWriter mapWriter;
private final Type keyType;
private final Type valueType;
final boolean valueTypeRefDetect;
volatile ObjectWriter valueWriter;
protected FieldWriterMap(
String name,
int ordinal,
long features,
String format,
Locale locale,
String label,
Type fieldType,
Class fieldClass,
Field field,
Method method,
Class<?> contentAs
) {
super(name, ordinal, features, format, locale, label, fieldType, fieldClass, field, method);
Type keyType = null, valueType = null;
Type contentAsFieldType = null;
if (fieldType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) fieldType;
Type[] actualTypeArguments = pt.getActualTypeArguments();
if (actualTypeArguments.length == 2) {
keyType = actualTypeArguments[0];
valueType = actualTypeArguments[1];
}
}
if (keyType == null) {
keyType = Object.class;
}
if (valueType == null) {
valueType = Object.class;
}
if (contentAs != null) {
contentAsFieldType = new ParameterizedTypeImpl(fieldClass, String.class, contentAs);
}
this.contentAs = contentAs;
this.contentAsFieldType = contentAsFieldType;
this.keyType = keyType;
this.valueType = valueType;
this.valueTypeRefDetect = !ObjectWriterProvider.isNotReferenceDetect(TypeUtils.getClass(valueType));
}
@Override
public ObjectWriter getObjectWriter(JSONWriter jsonWriter, Class valueClass) {
Class<?> contentAs = this.contentAs;
if (contentAs == null || !fieldClass.isAssignableFrom(valueClass)) {
return super.getObjectWriter(jsonWriter, valueClass);
}
ObjectWriter valueWriter = this.valueWriter;
if (valueWriter != null) {
return valueWriter;
}
Type fieldType = this.fieldType;
Type valueType = this.valueType;
long features = this.features;
if (contentAs != null) {
valueType = contentAs;
fieldType = contentAsFieldType;
features |= FieldInfo.CONTENT_AS;
}
valueWriter = new ObjectWriterImplMap(keyType, valueType, format, valueClass, fieldType, features);
this.mapWriter = valueWriter;
return valueWriter;
}
}

View File

@ -0,0 +1,25 @@
package com.alibaba.fastjson2.writer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Locale;
final class FieldWriterMapField
extends FieldWriterMap {
FieldWriterMapField(
String name,
int ordinal,
long features,
String format,
Locale locale,
String label,
Type fieldType,
Class fieldClass,
Field field,
Method method,
Class<?> contentAs
) {
super(name, ordinal, features, format, locale, label, fieldType, fieldClass, field, method, contentAs);
}
}

View File

@ -0,0 +1,39 @@
package com.alibaba.fastjson2.writer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Locale;
import java.util.function.Function;
final class FieldWriterMapFunction
extends FieldWriterMap {
final Function function;
FieldWriterMapFunction(
String name,
int ordinal,
long features,
String format,
Locale locale,
String label,
Type fieldType,
Class fieldClass,
Field field,
Method method,
Function function,
Class<?> contentAs
) {
super(name, ordinal, features, format, locale, label, fieldType, fieldClass, field, method, contentAs);
this.function = function;
}
@Override
public Object getFieldValue(Object object) {
return function.apply(object);
}
@Override
public Function getFunction() {
return function;
}
}

View File

@ -0,0 +1,37 @@
package com.alibaba.fastjson2.writer;
import com.alibaba.fastjson2.JSONException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Locale;
final class FieldWriterMapMethod
extends FieldWriterMap {
FieldWriterMapMethod(
String name,
int ordinal,
long features,
String format,
Locale locale,
String label,
Type fieldType,
Class fieldClass,
Field field,
Method method,
Class<?> contentAs
) {
super(name, ordinal, features, format, locale, label, fieldType, fieldClass, field, method, contentAs);
}
@Override
public Object getFieldValue(Object object) {
try {
return method.invoke(object);
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
throw new JSONException("invoke getter method error, " + fieldName, e);
}
}
}

View File

@ -97,7 +97,7 @@ public class FieldWriterObject<T>
}
}
private ObjectWriter getObjectWriterVoid(JSONWriter jsonWriter, Class valueClass) {
protected final ObjectWriter getObjectWriterVoid(JSONWriter jsonWriter, Class valueClass) {
ObjectWriter formattedWriter = null;
if (BeanUtils.isExtendedMap(valueClass) && SUPER.equals(fieldName)) {
JSONWriter.Context context = jsonWriter.context;

View File

@ -541,6 +541,12 @@ public class ObjectWriterBaseModule
fieldInfo.valueUsing = valueUsing;
}
break;
case "contentAs":
Class<?> contentAs = (Class) result;
if (contentAs != Void.class) {
fieldInfo.contentAs = contentAs;
}
break;
default:
break;
}
@ -967,6 +973,11 @@ public class ObjectWriterBaseModule
if (ObjectWriter.class.isAssignableFrom(serializeUsing)) {
fieldInfo.writeUsing = serializeUsing;
}
Class contentAs = jsonField.contentAs();
if (contentAs != Void.class) {
fieldInfo.contentAs = contentAs;
}
}
/**

View File

@ -258,7 +258,17 @@ public class ObjectWriterCreator {
format = beanInfo.format;
}
return createFieldWriter(provider, fieldName, fieldInfo.ordinal, fieldInfo.features, format, fieldInfo.locale, fieldInfo.label, field, writeUsingWriter);
return createFieldWriter(
provider,
fieldName,
fieldInfo.ordinal,
fieldInfo.features,
format,
fieldInfo.locale,
fieldInfo.label,
field,
writeUsingWriter,
fieldInfo.contentAs);
}
public ObjectWriter createObjectWriter(
@ -456,7 +466,8 @@ public class ObjectWriterCreator {
fieldInfo.format,
fieldInfo.label,
method,
writeUsingWriter
writeUsingWriter,
fieldInfo.contentAs
);
} catch (Throwable ignored) {
jitErrorCount.incrementAndGet();
@ -471,9 +482,11 @@ public class ObjectWriterCreator {
fieldInfo.ordinal,
fieldInfo.features,
fieldInfo.format,
fieldInfo.locale,
fieldInfo.label,
method,
writeUsingWriter
writeUsingWriter,
fieldInfo.contentAs
);
}
@ -732,6 +745,32 @@ public class ObjectWriterCreator {
String label,
Field field,
ObjectWriter initObjectWriter
) {
return createFieldWriter(
provider,
fieldName,
ordinal,
features,
format,
locale,
label,
field,
initObjectWriter,
null
);
}
public <T> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
String fieldName,
int ordinal,
long features,
String format,
Locale locale,
String label,
Field field,
ObjectWriter initObjectWriter,
Class<?> contentAs
) {
Class<?> declaringClass = field.getDeclaringClass();
Method method = null;
@ -859,7 +898,11 @@ public class ObjectWriterCreator {
if (fieldType instanceof ParameterizedType) {
itemType = ((ParameterizedType) fieldType).getActualTypeArguments()[0];
}
return new FieldWriterListField(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field);
return new FieldWriterListField(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field, contentAs);
}
if (Map.class.isAssignableFrom(fieldClass)) {
return new FieldWriterMapField(fieldName, ordinal, features, format, locale, label, field.getGenericType(), fieldClass, field, null, contentAs);
}
if (fieldClass.isArray() && !fieldClass.getComponentType().isPrimitive()) {
@ -867,7 +910,7 @@ public class ObjectWriterCreator {
return new FieldWriterObjectArrayField(fieldName, itemClass, ordinal, features, format, label, itemClass, fieldClass, field);
}
return new FieldWriterObject(fieldName, ordinal, features, format, null, label, field.getGenericType(), fieldClass, field, null);
return new FieldWriterObject(fieldName, ordinal, features, format, locale, label, field.getGenericType(), fieldClass, field, null);
}
public <T> FieldWriter<T> createFieldWriter(Class<T> objectType,
@ -907,7 +950,8 @@ public class ObjectWriterCreator {
format,
null,
label,
method, initObjectWriter
method,
initObjectWriter
);
}
@ -922,6 +966,22 @@ public class ObjectWriterCreator {
String label,
Method method,
ObjectWriter initObjectWriter
) {
return createFieldWriter(provider, objectType, fieldName, ordinal, features, format, locale, label, method, initObjectWriter, null);
}
public <T> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
Class<T> objectType,
String fieldName,
int ordinal,
long features,
String format,
Locale locale,
String label,
Method method,
ObjectWriter initObjectWriter,
Class<?> contentAs
) {
method.setAccessible(true);
Class<?> fieldClass = method.getReturnType();
@ -1021,7 +1081,11 @@ public class ObjectWriterCreator {
} else {
itemType = Object.class;
}
return new FieldWriterListMethod(fieldName, itemType, ordinal, features, format, label, null, method, fieldType, fieldClass);
return new FieldWriterListMethod(fieldName, itemType, ordinal, features, format, label, null, method, fieldType, fieldClass, contentAs);
}
if (Map.class.isAssignableFrom(fieldClass)) {
return new FieldWriterMapMethod(fieldName, ordinal, features, format, locale, label, fieldType, fieldClass, null, method, contentAs);
}
if (fieldClass == Float[].class || fieldClass == Double[].class || fieldClass == BigDecimal[].class) {
@ -1166,6 +1230,40 @@ public class ObjectWriterCreator {
Field field,
Method method,
Function<T, V> function
) {
return createFieldWriter(
provider,
objectClass,
fieldName,
ordinal,
features,
format,
locale,
label,
fieldType,
fieldClass,
field,
method,
function,
null
);
}
public <T, V> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
Class<T> objectClass,
String fieldName,
int ordinal,
long features,
String format,
Locale locale,
String label,
Type fieldType,
Class<V> fieldClass,
Field field,
Method method,
Function<T, V> function,
Class<?> contentAs
) {
if (fieldClass == Byte.class) {
return new FieldWriterInt8Func(fieldName, ordinal, features, format, label, field, method, function);
@ -1249,9 +1347,13 @@ public class ObjectWriterCreator {
if (itemType == String.class) {
return new FieldWriterListStrFunc(fieldName, ordinal, features, format, label, field, method, function, fieldType, fieldClass);
}
return new FieldWriterListFunc(fieldName, ordinal, features, format, label, itemType, field, method, function, fieldType, fieldClass);
return new FieldWriterListFunc(fieldName, ordinal, features, format, label, itemType, field, method, function, fieldType, fieldClass, contentAs);
}
}
if (rawType instanceof Class && Map.class.isAssignableFrom((Class) rawType)) {
return new FieldWriterMapFunction(fieldName, ordinal, features, format, locale, label, fieldType, fieldClass, field, method, function, contentAs);
}
}
if (Modifier.isFinal(fieldClass.getModifiers())) {
@ -1368,7 +1470,8 @@ public class ObjectWriterCreator {
String format,
String label,
Method method,
ObjectWriter initObjectWriter
ObjectWriter initObjectWriter,
Class<?> contentAs
) {
Class<?> fieldClass = method.getReturnType();
Type fieldType = method.getGenericReturnType();
@ -1439,6 +1542,6 @@ public class ObjectWriterCreator {
}
Function function = (Function) lambda;
return createFieldWriter(provider, objectClass, fieldName, ordinal, features, format, label, fieldType, fieldClass, field, method, function);
return createFieldWriter(provider, objectClass, fieldName, ordinal, features, format, null, label, fieldType, fieldClass, field, method, function, contentAs);
}
}

View File

@ -323,7 +323,8 @@ public class ObjectWriterCreatorASM
fieldInfo.format,
fieldInfo.label,
method,
writeUsingWriter
writeUsingWriter,
fieldInfo.contentAs
);
} catch (Throwable e) {
jitErrorCount.incrementAndGet();
@ -341,7 +342,8 @@ public class ObjectWriterCreatorASM
fieldInfo.locale,
fieldInfo.label,
method,
writeUsingWriter
writeUsingWriter,
fieldInfo.contentAs
);
}
@ -4397,13 +4399,14 @@ public class ObjectWriterCreatorASM
Locale locale,
String label,
Field field,
ObjectWriter initObjectWriter
ObjectWriter initObjectWriter,
Class<?> contentAs
) {
Class<?> declaringClass = field.getDeclaringClass();
if (Throwable.class.isAssignableFrom(declaringClass)
|| declaringClass.getName().startsWith("java.lang")
) {
return super.createFieldWriter(provider, fieldName, ordinal, features, format, locale, label, field, initObjectWriter);
return super.createFieldWriter(provider, fieldName, ordinal, features, format, locale, label, field, initObjectWriter, contentAs);
}
Class<?> fieldClass = field.getType();
@ -4535,7 +4538,11 @@ public class ObjectWriterCreatorASM
if (fieldType instanceof ParameterizedType) {
itemType = ((ParameterizedType) fieldType).getActualTypeArguments()[0];
}
return new FieldWriterListField(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field);
return new FieldWriterListField(fieldName, itemType, ordinal, features, format, label, fieldType, fieldClass, field, contentAs);
}
if (Map.class.isAssignableFrom(fieldClass)) {
return new FieldWriterMapField(fieldName, ordinal, features, format, locale, label, field.getGenericType(), fieldClass, field, null, contentAs);
}
if (fieldClass.isArray()) {

View File

@ -1,6 +1,7 @@
package com.alibaba.fastjson2.writer;
import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.codec.FieldInfo;
import com.alibaba.fastjson2.filter.*;
import com.alibaba.fastjson2.util.*;
@ -54,6 +55,8 @@ public final class ObjectWriterImplMap
final char[] typeInfoUTF16;
final byte[] typeInfoUTF8;
final boolean contentAs;
public ObjectWriterImplMap(Class objectClass, long features) {
this(null, null, objectClass, objectClass, features);
}
@ -75,6 +78,7 @@ public final class ObjectWriterImplMap
} else {
this.valueTypeRefDetect = !ObjectWriterProvider.isNotReferenceDetect(TypeUtils.getClass(valueType));
}
contentAs = (features & FieldInfo.CONTENT_AS) != 0;
String typeName = TypeUtils.getTypeName(objectClass);
String typeInfoStr = "\"@type\":\"" + objectClass.getName() + "\"";
@ -288,7 +292,12 @@ public final class ObjectWriterImplMap
}
}
Class<?> valueClass = value.getClass();
Class valueClass;
if (contentAs) {
valueClass = (Class) this.valueType;
} else {
valueClass = value.getClass();
}
if (valueClass == String.class) {
jsonWriter.writeString((String) value);
continue;
@ -491,7 +500,12 @@ public final class ObjectWriterImplMap
}
jsonWriter.writeColon();
Class<?> valueClass = value.getClass();
Class valueClass;
if (contentAs) {
valueClass = (Class) this.valueType;
} else {
valueClass = value.getClass();
}
if (valueClass == String.class) {
jsonWriter.writeString((String) value);
continue;
@ -667,8 +681,13 @@ public final class ObjectWriterImplMap
if (value == null) {
jsonWriter.writeNull();
} else {
Class<?> valueType = value.getClass();
ObjectWriter valueWriter = jsonWriter.getObjectWriter(valueType);
Class valueClass;
if (contentAs) {
valueClass = (Class) this.valueType;
} else {
valueClass = value.getClass();
}
ObjectWriter valueWriter = jsonWriter.getObjectWriter(valueClass);
valueWriter.write(jsonWriter, value, fieldName, fieldType, this.features);
}
} finally {

View File

@ -21,6 +21,8 @@ public class MapGhostTest {
byte[] bytes1 = JSONB.toBytes(map, JSONWriter.Feature.WriteClassName, JSONWriter.Feature.FieldBased);
assertArrayEquals(bytes, bytes1);
System.out.println(JSONB.toJSONString(bytes, true));
MapGhost mapGhost = (MapGhost) JSONB.parseObject(
bytes, Object.class,
JSONReader.Feature.FieldBased,

View File

@ -0,0 +1,111 @@
package com.alibaba.fastjson2.features;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.annotation.JSONField;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ContentAsTest {
class Vehicle {
private String type;
public Vehicle(String type) { this.type = type; }
public String getType() { return type; }
}
class Car
extends Vehicle {
private int seats;
public Car(String type, int seats) {
super(type);
this.seats = seats;
}
public int getSeats() { return seats; }
}
class Garage {
@JSONField(contentAs = Vehicle.class)
private List<Vehicle> vehicles = new ArrayList<>();
public void addVehicle(Vehicle v) { vehicles.add(v); }
public List<Vehicle> getVehicles() { return vehicles; }
}
@Test
public void test() throws Exception {
Garage garage = new Garage();
garage.addVehicle(new Car("Sedan", 5));
garage.addVehicle(new Car("SUV", 7));
assertEquals(
"{\"vehicles\":[{\"type\":\"Sedan\"},{\"type\":\"SUV\"}]}",
JSON.toJSONString(garage));
}
class GarageField {
@JSONField(contentAs = Vehicle.class)
public List<Vehicle> vehicles = new ArrayList<>();
public void addVehicle(Vehicle v) { vehicles.add(v); }
}
@Test
public void testField() throws Exception {
GarageField garage = new GarageField();
garage.addVehicle(new Car("Sedan", 5));
garage.addVehicle(new Car("SUV", 7));
assertEquals(
"{\"vehicles\":[{\"type\":\"Sedan\"},{\"type\":\"SUV\"}]}",
JSON.toJSONString(garage));
}
class GarageMap {
@JSONField(contentAs = Vehicle.class)
private Map<String, Vehicle> vehicles = new LinkedHashMap<>();
public void addVehicle(Vehicle v) {
vehicles.put(v.getType(), v);
}
public Map<String, Vehicle> getVehicles() {
return vehicles;
}
}
@Test
public void testMap() throws Exception {
GarageMap garage = new GarageMap();
garage.addVehicle(new Car("Sedan", 5));
garage.addVehicle(new Car("SUV", 7));
String json = JSON.toJSONString(garage);
assertEquals("{\"vehicles\":{\"Sedan\":{\"type\":\"Sedan\"},\"SUV\":{\"type\":\"SUV\"}}}", json);
}
class GarageMapField {
@JSONField(contentAs = Vehicle.class)
public Map<String, Vehicle> vehicles = new LinkedHashMap<>();
public void addVehicle(Vehicle v) {
vehicles.put(v.getType(), v);
}
}
@Test
public void testMapField() throws Exception {
GarageMapField garage = new GarageMapField();
garage.addVehicle(new Car("Sedan", 5));
garage.addVehicle(new Car("SUV", 7));
String json = JSON.toJSONString(garage);
assertEquals("{\"vehicles\":{\"Sedan\":{\"type\":\"Sedan\"},\"SUV\":{\"type\":\"SUV\"}}}", json);
}
}

View File

@ -0,0 +1,133 @@
package com.alibaba.fastjson2.jackson_support;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class JsonSerializeTest {
class Vehicle {
private String type;
public Vehicle(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
class Car
extends Vehicle {
private int seats;
public Car(String type, int seats) {
super(type);
this.seats = seats;
}
public int getSeats() {
return seats;
}
}
class Garage {
@JsonSerialize(contentAs = Vehicle.class)
private List<Vehicle> vehicles = new ArrayList<>();
public void addVehicle(Vehicle v) {
vehicles.add(v);
}
public List<Vehicle> getVehicles() {
return vehicles;
}
}
@Test
public void test() throws Exception {
Garage garage = new Garage();
garage.addVehicle(new Car("Sedan", 5));
garage.addVehicle(new Car("SUV", 7));
ObjectMapper mapper = new ObjectMapper();
String jackson = mapper.writeValueAsString(garage);
assertEquals("{\"vehicles\":[{\"type\":\"Sedan\"},{\"type\":\"SUV\"}]}", jackson);
assertEquals(jackson, JSON.toJSONString(garage));
}
class GarageMap {
@JsonSerialize(contentAs = Vehicle.class)
private Map<String, Vehicle> vehicles = new LinkedHashMap<>();
public void addVehicle(Vehicle v) {
vehicles.put(v.getType(), v);
}
public Map<String, Vehicle> getVehicles() {
return vehicles;
}
}
@Test
public void testMap() throws Exception {
GarageMap garage = new GarageMap();
garage.addVehicle(new Car("Sedan", 5));
garage.addVehicle(new Car("SUV", 7));
ObjectMapper mapper = new ObjectMapper();
String jackson = mapper.writeValueAsString(garage);
assertEquals("{\"vehicles\":{\"Sedan\":{\"type\":\"Sedan\"},\"SUV\":{\"type\":\"SUV\"}}}", jackson);
assertEquals(jackson, JSON.toJSONString(garage));
}
class GarageField {
@JsonSerialize(contentAs = Vehicle.class)
public List<Vehicle> vehicles = new ArrayList<>();
public void addVehicle(Vehicle v) {
vehicles.add(v);
}
}
@Test
public void testField() throws Exception {
GarageField garage = new GarageField();
garage.addVehicle(new Car("Sedan", 5));
garage.addVehicle(new Car("SUV", 7));
ObjectMapper mapper = new ObjectMapper();
String jackson = mapper.writeValueAsString(garage);
assertEquals("{\"vehicles\":[{\"type\":\"Sedan\"},{\"type\":\"SUV\"}]}", jackson);
assertEquals(jackson, JSON.toJSONString(garage));
}
class GarageMapField {
@JsonSerialize(contentAs = Vehicle.class)
public Map<String, Vehicle> vehicles = new LinkedHashMap<>();
public void addVehicle(Vehicle v) {
vehicles.put(v.getType(), v);
}
}
@Test
public void testMapField() throws Exception {
GarageMapField garage = new GarageMapField();
garage.addVehicle(new Car("Sedan", 5));
garage.addVehicle(new Car("SUV", 7));
ObjectMapper mapper = new ObjectMapper();
String jackson = mapper.writeValueAsString(garage);
assertEquals("{\"vehicles\":{\"Sedan\":{\"type\":\"Sedan\"},\"SUV\":{\"type\":\"SUV\"}}}", jackson);
assertEquals(jackson, JSON.toJSONString(garage));
}
}