Java 中 Snack3的使用
网上看了一篇Java 中 Gson的使用,所以也跟着写篇Java 中 Snack3的使用
JSON 是一种文本形式的数据交换格式,从Ajax的时候开始流行,它比XML更轻量、比二进制容易阅读和编写;解析和生成的方式很多,Java中最常用的类库有:JSON-Java、Gson、Jackson、FastJson、Snack3等。
Snack3 基于jdk8,60kb大小,非常小巧。
<dependency>
<groupId>org.noear</groupId>
<artifactId>snack3</artifactId>
<version>3.1.5.11</version>
</dependency>
Snack3 借鉴了 Javascript
所有变量由 var
申明,及 Xml dom
一切都是 Node
的设计。其下一切数据都以ONode
表示,ONode
也即 One node
之意,代表任何类型,也可以转换为任何类型。
- 强调文档树的操控和构建能力
- 做为中间媒体,方便不同格式互转
- 高性能
Json path
查询(兼容性和性能很赞) - 支持
序列化、反序列化
一、Snack3的基本用法
Snack3
提供了几个快捷函数:
load(strOrObj)
,loadStr(str)
,loadObj(obj)
用于解析和加载;stringify(obj)
,serialize(obj)
,deserialize(str,clz)
用于序列化和反序列化
(1)基本数据类型的解析
int i = ONode.load("100").getInt(); //100
double d = ONode.load("\"99.99\"").getDouble(); //99.99
boolean b = ONode.load("true").getBoolean(); // true
String str = ONode.load("String").getString(); // String
(2)基本数据类型的生成
String jsonNumber = ONode.load(100).toJson(); // 100
String jsonBoolean = ONode.load(false).toJson(); // false
String jsonString = ONode.load("String").toString(); //"String"
(3)POJO类的生成与解析
public class User {
public String name;
public int age;
public String emailAddress;
}
生成JSON:
User user = new User("张三",24);
//输出: {"name":"张三","age":24}
String json = ONode.stringify(user); //JSON字符化
//输出: {"@type":"demo.User","name":"\u5F20\u4E09","age":24}
String json2 = ONode.serialize(user); //JSON序列化
解析并反序列化JSON:
String json = "{name:'张三',age:24}";
User user = ONode.deserialize(json, User.class);//JSON反序列化
二、序列化事项补充说明
从上面示例可以看出json的字段和值是的名称和类型是一一对应的,Snack3不支持直接改名称,但可以通过transient
关键字进行排序,例:
public class User {
public String name;
public int age;
public String emailAddress;
public transient Date date;
}
或者,加载后再重改名称,例:
User user = new User("name", 12, "xxx@mail.cn");
//输出: {"name":"name","age":12,"email":"xxx@mail.cn"}
ONode.load(user).rename("emailAddress","email").toJson();
//o.rename(key,newKey); //重命名子节点,并返回自己
Snack3在序列化和反序列化时需要使用反射,且只对字段进行序列化(不管属性)。
特性总结:
- 只对字段进行序列化(包括私有)
- 使用
transient
对字段排序 - 加载后可进行重命名字段
三、Snack3中使用泛型
例如:JSON字符串数组:["Android","Java","PHP"]
当要通过Snack3解析这个json时,一般有三种方式:使用ONode,使用数组,使用List;而List对于增删都是比较方便的,所以实际使用是还是List比较多
数组比较简单:
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = ONode.deserialize(jsonArray,String[].class);
对于List将上面的代码中的 String[].class 直接改为 List<String>.class
是不行的,对于Java来说List<String>
和List<User>
这俩个的字节码文件只一个那就是List.class
,这是Java泛型使用时要注意的问题 泛型擦除。
为了解决的上面的问题,Snack3提供了2种方式来实现对泛型的支持,所以将以上的数据解析为List<String>
时需要这样写:
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
ONode ary0 = ONode.load(jsonArray);
List<String> ary1 = ONode.deserialize(jsonArray,(new ArrayList<String>(){}).getClass());
List<String> ary2 = ONode.deserialize(jsonArray,(new TypeRef<List<String>>(){}).getClass());
//(new ArrayList<String>(){}).getClass() //方式1,通过临时类形(最终都是产生Class)
//(new TypeRef<List<String>>(){}).getClass() //方式2,通过TypeRef(最终都是产生Class)
泛型的引入还可以减少无关的代码:
{"code":"0","message":"success","data":{}}
{"code":"0","message":"success","data":[]}
像上面这段代码,code只使用一次,message则几乎不用;我们真正需要的data所包含的数据,但它可能对象也可能是数组。如果Snack3不支持泛型或不知道Snack3支持泛型的同学一定会这么定义POJO:
public class UserResponse {
public int code;
public String message;
public User data;
}
当设计其它接口的时候又重新定义一个XxxResponse将data的类型改成Xxx,很明显code,和message被重复定义了多次,会产大量的POJO。。。通过泛型可以将code和message字段抽取到一个Result的类中,这样只需要编写data字段所对应的POJO即可:
public class Result<T> {
public int code;
public String message;
public T data;
}
四、Snack3的序列化与反序列化
(1)自动方式
Snack3提供了serialize(obj)
和deserialize(str,clz)
前者实现序列化,后者实现了反序列化。
ONode.serialize(obj); //序列化
ONode.deserialize(str,clz); //反序列化
(2)手动方式:
手动的方式使用load(obj)
,toObject(clz)
,toJson()
来手动实现序列化和反序列化:
String json = "{\"name\":\"张三\",\"age\":\"24\"}";
//反序列化
User user = ONode.load(json,Constants.serialize()).toObject(User.class);
//序列化
ONode.load(user,Constants.serialize()).toJson();
自动方式最终都是通过load(obj)
,toObject(clz)
,toJson()
进行操作。
内部代码:
/**
* 序列化为 string(由序列化器决定格式)
*
* @param source java object
* @throws Exception
*/
public static String serialize(Object source) {
//加载java object,须指定Fromer
return load(source, Constants.serialize(), DEFAULTS.DEF_OBJECT_FROMER).toJson();
}
/**
* 反序列化为 java object(由返序列化器决定格式)
*
* @param source string
* @throws Exception
*/
public static <T> T deserialize(String source, Class<?> clz) {
//加载String,不需指定Fromer
return load(source, Constants.serialize(), null).toObject(clz);
}
五、使用Snack3导出null值、格式化输出、日期时间
一般情况下ONode类提供的 API已经能满足大部分的使用场景,但有时需要更多特殊、强大的功能时,这时候就引入一个新的类 Constants。
Constants从名字上看它是一个提供配置的类,要想改变ONode默认的设置必须使用该类进行配置。用法:
Constants.of(..) //全新定义一份配置
Constants.def().add(..).sub(..) //在默认配置基础上,添加或减少特性
Constants.serialize().add(..).sub(..) //在序列化配置基础上,添加或减少特性
(1)Snack3在默认情况下是不动导出值null的键的,如:
User user = new User("张三", 24);
System.out.println(ONode.stringify(user)); //{"name":"张三","age":24}
Constants cfg = Constants.def().add(Feature.SerializeNulls); //导出null
System.out.println(ONode.load(user, cfg).toJson()); //{"name":"张三","age":24,"emailAddress":null}
(2)格式化输出、日期时间及其它:
Date date = new Date();
Constants cfg = Constants.of(Feature.WriteDateUseFormat) //使用格式化特性
.build(c-> c.date_format = new SimpleDateFormat("yyyy-MM-dd",c.locale)); //设置格式符(默认为:"yyyy-MM-dd'T'HH:mm:ss")
System.out.println(ONode.load(date, cfg).toJson()); //2019-12-06
六、使用Snack3进行JSONPath查询
在网上找了一份经典的JSON样本:
{
"store": {
"bicycle": {
"color": "red",
"price": 19.95
},
"book": [
{
"author": "刘慈欣",
"price": 8.95,
"category": "科幻",
"title": "三体"
},
{
"author": "itguang",
"price": 12.99,
"category": "编程语言",
"title": "go语言实战"
}
]
}
}
Snack3可以提供高速的JSONPath查询,JSONPath更给日常的查询节省了大量代码:
ONode o = ONode.load(jsonStr);
//得到所有的书
ONode books = o.select("$.store.book");
System.out.println("books=::" + books);
//得到所有的书名
ONode titles = o.select("$.store.book.title");
System.out.println("titles=::" + titles);
//第一本书title
ONode title = o.select("$.store.book[0].title");
System.out.println("title=::" + title);
//price大于10元的book
ONode list = o.select("$.store.book[?(price > 10)]");
System.out.println("price大于10元的book=::" + list);
//price大于10元的title
ONode list2 = o.select("$.store.book[?(price > 10)].title");
System.out.println("price大于10元的title=::" + list2);
//category(类别)为科幻的book
ONode list3 = o.select("$.store.book[?(category == '科幻')]");
System.out.println("category(类别)为科幻的book=::" + list3);
//bicycle的所有属性值
ONode values = o.select("$.store.bicycle.*");
System.out.println("bicycle的所有属性值=::" + values);
//bicycle的color和price属性值
ONode read = o.select("$.store.bicycle['color','price']");
System.out.println("bicycle的color和price属性值=::" + read);
(1)支持的JSONPath语法
- 字符串使用单引号,例:['name']
- 过滤操作用空隔号隔开,例:[?(@.type == 1)]
支持操作 | 说明 |
---|---|
$ |
表示根元素 |
@ |
当前节点(做为过滤表达式的谓词使用) |
* |
通用配配符,可以表示一个名字或数字。 |
.. |
深层扫描。 可以理解为递归搜索。 |
.<name> |
表示一个子节点 |
['<name>' (, '<name>')] |
表示一个或多个子节点 |
[<number> (, <number>)] |
表示一个或多个数组下标(负号为倒数) |
[start:end] |
数组片段,区间为[start,end),不包含end(负号为倒数) |
[?(<expression>)] |
过滤表达式。 表达式结果必须是一个布尔值。 |
支持过滤操作符 | 说明 |
---|---|
== |
left等于right(注意1不等于'1') |
!= |
不等于 |
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
=~ |
匹配正则表达式[?(@.name =~ /foo.*?/i)] |
in |
左边存在于右边 [?(@.size in ['S', 'M'])] |
nin |
左边不存在于右边 |
支持尾部函数 | 说明 |
---|---|
min() |
计算数字数组的最小值 |
max() |
计算数字数组的最大值 |
avg() |
计算数字数组的平均值 |
sum() |
计算数字数组的汇总值(新加的) |
像这两种写法的语义是差不多:
$.store.book[0].title //建议使用这种
$['store']['book'][0]['title']
(2)语法示例说明
JSONPath | 说明 |
---|---|
$ |
根对象 |
$[-1] |
最后元素 |
$[:-2] |
第0个至倒数第2个 |
$[1:] |
第1个之后所有元素(0为首个) |
$[1,2,3] |
集合中1,2,3个元素(0为首个) |
七、数据格式互转
Snack3是采用(Fromer)
->(ONode)
->(Toer)
的架构。非常适合格式的转换,开发时只需要完成与ONode的对接即可:
(1)将Xml转为Ymal
String xml = "<xml>....</xml>";
XmlFromer xmlFromer = new XmlFromer();
YmalToer ymalToer = new YmalToer();
//加载xml,输出ymal
String ymal = ONode.load(xml,Constants.def(),xmlFromer).to(ymalToer);
(2)加载Xml,去掉手机号,转为java object
ONode tmp =ONode.load(xml,Constants.def(),xmlFromer);
//找到有手机号的,然后移除手机号
tmp.select("$..[?(@.mobile)]").forEach(n->n.remove("mobile"));
XxxModel m =tmp.toObject(XxxModel.class);
Java 中 Snack3的使用的更多相关文章
- java中的锁
java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...
- java中的字符串相关知识整理
字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...
- Java中的Socket的用法
Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...
- java中Action层、Service层和Dao层的功能区分
Action/Service/DAO简介: Action是管理业务(Service)调度和管理跳转的. Service是管理具体的功能的. Action只负责管理,而Service负责实施. DAO只 ...
- Java中常用集合操作
一.Map 名值对存储的. 常用派生类HashMap类 添加: put(key,value)往集合里添加数据 删除: clear()删除所有 remove(key)清除单个,根据k来找 获取: siz ...
- java中的移位运算符:<<,>>,>>>总结
java中有三种移位运算符 << : 左移运算符,num << 1,相当于num乘以2 >> : 右移运算符,num >& ...
- 关于Java中进程和线程的详解
一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...
- Java中的进程和线程
Java中的进程与线程 一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是 ...
- Java中的进程与线程(总结篇)
详细文档: Java中的进程与线程.rar 474KB 1/7/2017 6:21:15 PM 概述: 几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进 ...
随机推荐
- 学习笔记14Js使用技巧
**页面加载完毕时执行一段代码$(fuction(){ initialMethod(); }); *将一个<form>序列化为json对象并传给服务器var postData = $(&q ...
- WPF项目设计规则
关于WPF应用程序的设计: 项目构成,自上而下: 1. 前端的WPF应用程序项目. 2. 业务逻辑的类库项目. 3. 映射到数据库各个表的Model型类库,(数据库操作接口定义). 4. 其他工具型类 ...
- Unity入门--实用知识
目录 1. VS适配 2.实用快捷操作 3.Unity API文档 4.项目整理 1. VS适配 让你的VS完美支持Unity的脚本编写可以让你写起C#脚本来事半功倍,比如代码补全功能,可以参考下面这 ...
- Oracle基础:数据库操作_数据库事务_表的锁定
数据库操作语句: INSERT INTO 表名[(字段列表)] VALUES ( 表达式列表); 例子:INSERT INTO emp(empno,ename,job,hiredate) VALUES ...
- windows 激活工具链接
链接:https://pan.baidu.com/s/1FphGFZhhLp01akGTDWjW2A 密码:f9t7
- 易语言 史诗级Json处理 烁_Json模块!!!!
大家好,我是键盘上的魔手 * “************************”* “** 欢迎使用烁Json模块 **”* “** 作者:键盘上的魔手 **”* “** 微信号:codervip ...
- spark集群搭建(三台虚拟机)——spark集群搭建(5)
!!!该系列使用三台虚拟机搭建一个完整的spark集群,集群环境如下: virtualBox5.2.Ubuntu14.04.securecrt7.3.6_x64英文版(连接虚拟机) jdk1.7.0. ...
- pat 1013 Battle Over Cities(25 分) (并查集)
1013 Battle Over Cities(25 分) It is vitally important to have all the cities connected by highways i ...
- JQury自动切换图片
[标签]Jquery图片自动切换<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "ht ...
- Apache中AllowOverride的详细配置使用
我们通常利用Apache的rewrite模块对URL进行重写,rewrite规则会写在 .htaccess 文件里.但要使 apache 能够正常的读取.htaccess 文件的内容,就必须对.hta ...