Gson序列化中指定顺序的实现(按英文字段名升序)

需要输入的实体示例如下:

Gson序列化中指定顺序的实现(按英文字段名升序)

/**
 * 云打印请求参数
 */
@Data
公共类 YiRuiPrintResParam 继承自 YiRuiCommonResParam 并实现了 Serializable 接口。
    private String deviceName;
    private String token;
    private String transNo;
    private Integer type;
    private Nc nc;
    private String pc;
}

/**
 * 公共请求参数
 */
@Data
公共类 YiRuiCommonResParam 实现了 Serializable 接口。
    private String productKey;
    private Long timestamp;
    private String signMethod;
    private String sign;
}

@Data
public class Nc {
    private Integer channel;
    private String ct;
    private String charset;
}

如果没有指定顺序,Gson 会依据实体类里的字段排列顺序进行序列化,并且这种序列化与字段设值顺序无关。同时,实体类里面的类 Nc 的输出字段顺序也是按照实体类里的字段排列顺序来的。

序列化输出代码:

Gson printGson = new Gson();
使用 printGson 将 printParam 转换为 JSON 字符串,得到的结果赋值给 String 类型的 printReq 。
System.out.printf("云打印的请求为:%s\n", printReq);

序列化输出结果:

{
"deviceName"为"8115210800100022"。
"token"为"E75x+wmRF3eyo1IZxnn+s2PrhtAYIhX1Kchk9eVtx1niQulJx4QgUwYoYCe7Fugv1634089348814"。
    "transNo": "20211013141142",
    "type": 0,
    "nc": {
        "channel": 1,
        "ct": "12345",
        "charset": "abcde"
    },
"cn" 的值为 1。
"productKey"的值为"78d077bf960c477b"。
    "timestamp": 1634105502,
    "signMethod": "MD5",
"sign" 的值为 "394009CE36A0AD145D279AF84131E35D"
}

如果想要指定顺序,那该如何实现呢?我的需求是依据字段的英文名称进行升序排序。我参考了网络上的一些做法,这些做法大多倾向于通过继承 TypeAdapter 类来达成目的,具体的做法如下:

/**
 * 打印适配器
 */
公共类 YiRuiPrintAdapter 继承自 TypeAdapter 。 {
    @Override
公共的 void 方法 write 会将参数 out(JsonWriter 类型)和 param(YiRuiPrintResParam 类型)写入,并且在这个过程中不会抛出 IOException 异常。
//        ClassncClazz 等于 param.getNc() 的类;
ncClazz 的声明字段被获取到,这些字段是 Field 类型的数组,记为 ncFields 。
        out.beginObject();
        // 按自定义顺序输出字段信息
使用 out.name 并传入 "deviceName" ,然后获取 param 的设备名称并赋值给它;或者让 out.name 指向 "deviceName" ,接着获取 param 的设备名称并将其赋予 out.name ;又或者通过 out.name("deviceName") 来获取 param 的设备名称并进行相应操作。
        out.name("nc");
        out.beginObject();
调用 out.name 方法并传入 "channel" 作为参数,然后获取 param.getNc().getChannel 的值并作为 out.name 的返回值
获取名为"charset"的内容,然后获取 param 的 nc 属性,再获取该 nc 属性的字符集,最后将获取到的字符集设置为 out 的 name 属性的值。
调用 out 的 name 方法并传入 "ct",然后获取 param 的 getNc 方法返回的对象的 ct 属性值。
设置写入(ncFields,out,param.getNc());
        out.endObject();
调用 name 方法并传入 "pc",然后获取 param 的 pc 属性值。
调用 out.name 方法并传入 "productKey" 作为参数,获取其返回值,然后将 param 中的 getProductKey 方法的返回值作为 value 传入到前面获取的返回值中。
通过 name 为"sign"的方式,将 param.getSign()作为值进行传递。
调用 out.name 方法并传入参数 param.getSignMethod(),将其值赋给 signMethod 字段。
获取名为"timestamp"的名称。然后,使用 param 获取时间戳。最后,将获取到的时间戳设置为名为"timestamp"的值。
调用 out 的 name 方法并传入"token",然后获取 param 的 getToken 方法的值。
获取名为"transNo"的名称并将其值设置为参数 param 中的交易号 param.getTransNo()
调用 name("type") 方法并获取其返回值,然后将该返回值作为参数传递给 value 方法,参数为 param.getType() 的值。
        out.endObject();
    }
    @Override
公共的 YiRuiPrintResParam 读取(JsonReader jsonReader)会引发 IOException。
        return null;
    }
//    /**
//     * 递归写入属性
//     *
//     * @param fields
//     * @param out
//     * @param object
//     * @return void
作者是 liangzhaolinC。
在 2021 年 10 月 13 日 10 点 50 分这个时间点被标注为 @date。
//     */
公共的方法用于设置写入操作,它接收字段数组、 JSON 写入器以及对象作为参数。该方法的作用是进行相关的写入设置操作。
//        for (int i = 0; i < fields.length; i++) {
//            Field field = fields[i];
//            field.setAccessible(true);
//            try {
//                Object type = field.get(object);
//                if (type == null) {
//                    continue;
//                }
//                if (!(type instanceof String)) {
//                    out.name(field.getName()).beginObject();
//                    Field[] subFields = type.getClass().getDeclaredFields();
//                    setWrite(subFields, out, type);
//                    out.endObject();
//                } else {
//                    out.name(field.getName()).value(type.toString());
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
//    }
}

out.beginObject() 的含义是在 json 中添加左括号“{”。

out.endObject()的含义是在 json 中添加右括号 } 。

out.name("nc")的含义是在 json 中添加 key "nc"。

out.value(xxx)的含义是在 json 中添加 value"xxx"

可以看下对应里面的源码理解下。

代码中注释的部分,其目的是想自动打印出 Nc 类里的字段。然而,由于代码是参考网上的,在尝试过程中遇到了一些 bug。因为项目比较紧急,所以先将其注释掉,留待以后再进行研究。如果有小伙伴能够修复这个问题,也希望能分享一下。取而代之的是,通过写死的方式来控制 Nc 的字段序列化顺序。所以有第二次调用 out.beginObject(); ,以及与之对应的 out.endObject();

序列化代码:

Gson printGson 被创建出来,创建时使用了 new GsonBuilder(),并且在其中注册了类型适配器 YiRuiPrintResParam.class 对应的 YiRuiPrintAdapter(),最终通过 create()方法生成了这个 Gson 对象。
String printReq = printGson.toJson(printParam);
System.out.printf("云打印 request:%s\n", printReq);

序列化输出结果:

{
    "deviceName": "8115210800100022",
    "nc": {
        "channel": 1,
        "charset": "abcde",
        "ct": "12345"
    },
    "pc": "{\"ct\":[{\"cols\":[{\"cv\":\"\"}],\"tp\":\"0\"},{\"tp\":\"3\"},{\"cols\":[{\"cv\":\"商户名称:\"},{\"cv\":\"测试商户\"}],\"tp\":\"1\"},{\"cols\":[{\"cv\":\"金额RMB:\"},{\"cv\":\"0.01元\"}],\"tp\":\"1\"}],\"cn\":1}",
    "productKey": "78d077bf960c477b",
"sign" 的值为 "F6093E0B127D6B537D8BDD376C532097"。
    "signMethod": "MD5",
    "timestamp": 1634106351,
    "token": "E75x+wmRF3eyo1IZxnn+s2PrhtAYIhX1Kchk9eVtx1niQulJx4QgUwYoYCe7Fugv1634089348814",
    "transNo": "20211013142551",
    "type": 0
}

参考资料:

java自定义Gson生成json字符串的顺序

gson改变输出字段的顺序

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

27阅读

java中冒号,java双冒号

2026年09月06日

26阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

28阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

26阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

26阅读