前因
为什么要开发这个 JSON库?原因是 delphi 官方的 json 既没有处理 null(也叫零值)的问题;举例说明吧:
开发者 往往 需要 类与JSON 之间 进行序列化 和 反序列化;接下来我们举个例子:
Person {id: Int64; // IDname: 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 mainimport ("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, 使用指针赋值}//序列化为 JSONjsonData, err := json.Marshal(person)if err != nil {return}fmt.Println(string(jsonData)) //结果:{"id":"123","name":"欧阳疯"} 可见golang的序列化已给开发者忽略了 Nil值//反序列化var person1 Personerr = json.Unmarshal([]byte(jsonData), &person1)if err != nil {return}if person1.Desc != nil { //通过这样就可以判断是否是nilfmt.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;interfaceuses System.SysUtils;typeTPerson = classprivateid: PInt64;name: PString;desc: PString;publicdestructor 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;implementationconst//可以定义在公共单元里nullMsg = '开发人员错误,此字段可能为Null,获取前要进行fieldIsNull判断,从而给其默认值或做出处理!';destructor TPerson.Destroy;
beginif id <> nil thenbeginDispose(id);end;if name <> nil thenbeginDispose(name);end;if desc <> nil thenbeginDispose(desc);end;inherited;
end;function TPerson.idIsNull: Boolean;
beginResult := (id = nil);
end;function TPerson.getId: Int64;
beginif id = nil thenbeginraise Exception.Create(nullMsg);end;Result := id^;
end;procedure TPerson.setId(value: Int64);
beginif id = nil thenbeginNew(id);end;id^ := value;
end;function TPerson.nameIsNull: Boolean;
beginResult := (name = nil);
end;function TPerson.getName: string;
beginif name = nil thenbeginraise Exception.Create(nullMsg);end;Result := name^;
end;procedure TPerson.setName(value: string);
beginif name = nil thenbeginNew(name);end;name^ := value;
end;function TPerson.descIsNull: Boolean;
beginResult := (desc = nil);
end;function TPerson.getDesc: string;
beginif desc = nil thenbeginraise Exception.Create(nullMsg);end;Result := desc^;
end;procedure TPerson.setDesc(value: string);
beginif desc = nil thenbeginNew(desc);end;desc^ := value;
end;end.
-
开始序列化 和 反序列化
uses djson, person;procedure TFormMain.Button1Click(Sender: TObject); beginvar 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 thenbeginMemo1.Lines.Add('有设置');end else begin //说明是nullMemo1.Lines.Add('nil');end;person.Free;person2.Free;finallydj.Free;end; end;
可见写法 几乎 与 Java 一模一样了,与 golang 也基本一样!!!使用起来非常简单;关于 那个 person 类的代码生成,Java Ide 是可以轻松生成的,但是 delphi 的IDE 无法生成,你可以自己写一个 生成器(不是IDE插件),而是一个软件,专一生成这种格式的 delphi 类;这样以后 你就可以快速 生成 各种 delphi 类,并将其 与 json 互转,且有 ”零值" 如:nameIsNull 的处理;另外我上面给的这个例子是简单的,你完全可以类与类互相嵌套,组成复杂的 类 ,依然可以 与 JSON 互转,包括 array、枚举等 都可以互相嵌套的;我下一篇博客会做一个 复杂的 嵌套类的例子;