场景:
将对象内的属性按属性名称先后排序,如果属性依然是对象,继续按照这个规则对这个对象内的属性进行排序
错误示例:
只将首层对象进行排序了
下面是示例对象
@Test
public void test() {
List<Ab> list = new ArrayList<>();
list.add(new Ab("1", "4", "7"));
list.add(new Ab("2", "5", "7"));
list.add(new Ab("3", "6", "7"));
List<String> strings = new ArrayList<>();
strings.add("111");
strings.add("222");
Aa a = new Aa("aa", "man", list, strings);
String str1 = DataUtils.marshal(a);
System.out.println(str1);
}
public class Aa{
String name;
String sex;
List<Ab> list;
List<String> listStr;
public Aa(String name, String sex, List<Ab> list, List<String> listStr) {
this.name = name;
this.sex = sex;
this.list = list;
this.listStr = listStr;
}
}
public class Ab{
String nameDebug;
String sexDebug;
String address;
public Ab(String nameDebug, String sexDebug, String address) {
this.nameDebug = nameDebug;
this.sexDebug = sexDebug;
this.address = address;
}
}
得到的错误结果是
{“list”:[{“nameDebug”:“1”,“sexDebug”:“4”,“address”:“7”},{“nameDebug”:“2”,“sexDebug”:“5”,“address”:“7”,},{“nameDebug”:“3”,“sexDebug”:“6”,“address”:“7”}],“listStr”:[“111”,“222”],“name”:“aa”,“sex”:“man”}
这个结果内的list属性对象内并未做出排序
以下是排序代码
// 序列化参数
// 这一步看上去冗余,实际很重要。如果要自己实现,则必须保证这三点:
// 1、保证JSON所有层级上Key的有序性
// 2、保证JSON的所有数值不带多余的小数点
// 3、保证转义逻辑与这段代码一致
public static String marshal(Object o) {
String raw = CustomGson.toJson(o);
Map<?, ?> m = CustomGson.fromJson(raw, LinkedTreeMap.class); // 执行反序列化,把所有JSON对象转换成LinkedTreeMap
return CustomGson.toJson(m); // 重新序列化,保证JSON所有层级上Key的有序性
}
private static final Gson CustomGson = new GsonBuilder()
.registerTypeAdapter(LinkedTreeMap.class, newMapSerializer()) // 定制LinkedTreeMap序列化,确保所有key按字典序排序
.registerTypeAdapter(Integer.class, newNumberSerializer()) // 定制数值类型的序列化,确保整数输出不带小数点
.registerTypeAdapter(Long.class, newNumberSerializer()) // 同上
.registerTypeAdapter(Double.class, newNumberSerializer()) // 同上
.disableHtmlEscaping() // 禁用Html Escape,确保符号不转义:&<>='
.create();
// 为LinkedTreeMap定制的序列化器
public static JsonSerializer<LinkedTreeMap<?, ?>> newMapSerializer() {
return new JsonSerializer<LinkedTreeMap<?,?>>() {
@Override
public JsonElement serialize(LinkedTreeMap<?, ?> src, Type typeOfSrc, JsonSerializationContext context) {
List<String> keys = src.keySet().stream().map(Object::toString).sorted().collect(Collectors.toList());
JsonObject o = new JsonObject();
for (String k : keys) {
o.add(k, context.serialize(src.get(k)));
}
return o;
}
};
}
// 为Number定制化的序列化器
private static <T extends Number> JsonSerializer<T> newNumberSerializer() {
return new JsonSerializer<T>() {
@Override
public JsonElement serialize(T number, Type type, JsonSerializationContext context) {
if (number instanceof Integer) {
return new JsonPrimitive(number.intValue());
}
if (number instanceof Long) {
return new JsonPrimitive(number.longValue());
}
if (number instanceof Double) {
long longValue = number.longValue();
double doubleValue = number.doubleValue();
if (longValue == doubleValue) {
return new JsonPrimitive(longValue);
}
}
return new JsonPrimitive(number);
}
};
}
正确输出的结果是:
{“list”:[{“address”:“7”,“nameDebug”:“1”,“sexDebug”:“4”},{“address”:“7”,“nameDebug”:“2”,“sexDebug”:“5”},{“address”:“7”,“nameDebug”:“3”,“sexDebug”:“6”}],“listStr”:[“111”,“222”],“name”:“aa”,“sex”:“man”}
主要调用方法
DataUtils.marshal(a);