delphi djson 类与JSON 互转,与 Java、Golang 一致写法
前因
为什么要开发这个 JSON库?原因是 delphi 官方的 json 既没有处理 null(也叫零值)的问题;举例说明吧:
开发者 往往 需要 类与JSON 之间 进行序列化 和 反序列化;接下来我们举个例子:
Person {
id: Int64; // ID
name: string; //姓名
desc: string; //描述
}
这样一个类 在 不同 语言里 如何 与 json 进行序列化 和 反序列化;
java 语言
在Java中,我们使用 json 往往很方便,因为 Java的 基本类型是包装类,具有自动装箱和拆箱功能,可以轻易解决 零值的问题;fastjson gson jackson 都可以,以 jackson举例:
public class Person {
private Long id;
private String name;
private String desc;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
开始序列化和反序列化:
public class Test {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
Person person = new Person();
person.setId(123L);
person.setName("欧阳疯");
//person.setDesc(""); 不设置,让其忽略 null 值
//序列化
String json = objectMapper.writeValueAsString(person);
System.out.println(json); //{"id":123,"name":"欧阳锋"} 已忽略了 null值
//反序列化
Person person1 = objectMapper.readValue(json, Person.class);
if(person1.getDesc() != null){ //反序列化后通过这个可以判断是否是nil值
System.out.println("有设置");
}else {
//说明这个字段是未设置状态
System.out.println("nil");
}
}
}

golang 语言
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Id string `json:"id"`
Name string `json:"name,omitempty"`
Desc *string `json:"desc,omitempty"` //注意这里使用 *string + omitempty 来解决零值的问题
}
func main() {
// 赋值给 Person 结构体
person := Person{
Id: "123",
Name: "欧阳疯",
//Desc: &desc, 使用指针赋值
}
//序列化为 JSON
jsonData, err := json.Marshal(person)
if err != nil {
return
}
fmt.Println(string(jsonData)) //结果:{"id":"123","name":"欧阳疯"} 可见golang的序列化已给开发者忽略了 Nil值
//反序列化
var person1 Person
err = json.Unmarshal([]byte(jsonData), &person1)
if err != nil {
return
}
if person1.Desc != nil { //通过这样就可以判断是否是nil
fmt.Println("有设置")
} else {
fmt.Println("nil")
}
}

可见 golang 官方的 json库 针对 基本类型的指针类型,也自动给开发者专一处理了;也非常方便使用 json 与 结构体之间的转换;
delphi 语言
例子就不上了,目前无论是官方的 System.JSON 还是其它第三方的,都做不到 类 与 Json 的互转,并同时处理 ”零值“ 的问题;之所以不想上官方的例子了,是因为官方的 System.JSON 使用起来非常复杂,仅仅是一个 类的序列化 都需要很长的代码,甚至还得手工写字段名;估计这个 System.JSON 也没有人使用吧,为了节省篇幅,不上官方的例子了;
djson
在以上背景下,我们知道了 Java、C# 这类具有基本类型的包装类,自动拆箱和装箱的语言,对于JSON里的 null 值天生解决了;而对于 golang、c++、delphi 这类没有包装类的语言,就得想法解决了,最好是语言层面给解决;golang 就是这样,通过 指针 + golang官方的注解,就解决掉了 nil 的问题;而 delphi 目前官方的 json 库,并没有给出解决方案;于是就开发了 djson,设计JSON底层数据模型,并大量使用了 向量化(SIMD)指令(System.Math.Vectors)来加速;还是上面的那个例子,我们看下 djson 如何实现,用 delphi 可以实现类似 Java 的写法,也可以实现 类似 golang的写法,个人感觉 还是 Java的写法 更优雅些,决定采用 Java 的写法了;
- 与Java 一样,先定义一个 Person 类;
unit person;
interface
uses System.SysUtils;
type
TPerson = class
private
id: PInt64;
name: PString;
desc: PString;
public
destructor Destroy; override;
function idIsNull: Boolean;
function getId: Int64;
procedure setId(value: Int64);
function nameIsNull: Boolean;
function getName: string;
procedure setName(value: string);
function descIsNull: Boolean;
function getDesc: string;
procedure setDesc(value: string);
end;
implementation
const
//可以定义在公共单元里
nullMsg = '开发人员错误,此字段可能为Null,获取前要进行fieldIsNull判断,从而给其默认值或做出处理!';
destructor TPerson.Destroy;
begin
if id <> nil then
begin
Dispose(id);
end;
if name <> nil then
begin
Dispose(name);
end;
if desc <> nil then
begin
Dispose(desc);
end;
inherited;
end;
function TPerson.idIsNull: Boolean;
begin
Result := (id = nil);
end;
function TPerson.getId: Int64;
begin
if id = nil then
begin
raise Exception.Create(nullMsg);
end;
Result := id^;
end;
procedure TPerson.setId(value: Int64);
begin
if id = nil then
begin
New(id);
end;
id^ := value;
end;
function TPerson.nameIsNull: Boolean;
begin
Result := (name = nil);
end;
function TPerson.getName: string;
begin
if name = nil then
begin
raise Exception.Create(nullMsg);
end;
Result := name^;
end;
procedure TPerson.setName(value: string);
begin
if name = nil then
begin
New(name);
end;
name^ := value;
end;
function TPerson.descIsNull: Boolean;
begin
Result := (desc = nil);
end;
function TPerson.getDesc: string;
begin
if desc = nil then
begin
raise Exception.Create(nullMsg);
end;
Result := desc^;
end;
procedure TPerson.setDesc(value: string);
begin
if desc = nil then
begin
New(desc);
end;
desc^ := value;
end;
end.
-
开始序列化 和 反序列化
uses djson, person; procedure TFormMain.Button1Click(Sender: TObject); begin var dj := TDJson.Create; try //序列化 var person := TPerson.Create; person.setId(123); person.setName('欧阳疯'); //person.setDesc() 不设置 忽略 null 值 var json := dj.objToJson(person); //这一句 就行了 Memo1.Lines.Add(json); //序列化后的结果 //开始反序列化 var person2 := TPerson.Create; dj.jsonToObj(json, person2); if not person2.descIsNull then begin Memo1.Lines.Add('有设置'); end else begin //说明是null Memo1.Lines.Add('nil'); end; person.Free; person2.Free; finally dj.Free; end; end;
可见写法 几乎 与 Java 一模一样了,与 golang 也基本一样!!!使用起来非常简单;关于 那个 person 类的代码生成,Java Ide 是可以轻松生成的,但是 delphi 的IDE 无法生成,你可以自己写一个 生成器(不是IDE插件),而是一个软件,专一生成这种格式的 delphi 类;这样以后 你就可以快速 生成 各种 delphi 类,并将其 与 json 互转,且有 ”零值" 如:nameIsNull 的处理;另外我上面给的这个例子是简单的,你完全可以类与类互相嵌套,组成复杂的 类 ,依然可以 与 JSON 互转,包括 array、枚举等 都可以互相嵌套的;我下一篇博客会做一个 复杂的 嵌套类的例子;

浙公网安备 33010602011771号