目录
目录
4.5 MappingProvider SPI反序列化器编辑
4.8.1 DEFAULT_PATH_LEAF_TO_NULL
一、前言
本文主要包含: JSON基础、JSON数据类型、JsonPath的使用、日常使用的坑点
二、JSON基础
2.1 什么是JSON?
JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式。它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
2.2 JSON 语法规则
JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。
JSON是一个序列化的对象或数组。
六个构造字符:
begin-array = ws %x5B ws ; [ 左方括号
begin-object = ws %x7B ws ; { 左大括号
end-array = ws %x5D ws ; ] 右方括号
end-object = ws %x7D ws ; } 右大括号
name-separator = ws %x3A ws ; : 冒号
value-separator = ws %x2C ws ; , 逗号
在这六个构造字符的前或后允许存在无意义的空白符(ws):
ws = *(%x20 /; 空间
%x09 /; 水平标签
%x0A /; 换行或换行
%x0D); 回程
2.3 JSON的值
值可以是对象、数组、数字、字符串或者三个字面值(false、null、true)中的一个。值中的字面值中的英文必须使用小写。
对象由花括号括起来的逗号分割的成员构成,成员是字符串键和上文所述的值由逗号分割的键值对组成,如:
1
{
"name"
:
"John Doe"
,
"age"
: 18,
"address"
: {
"country"
:
"china"
,
"zip-code"
:
"10000"
}}
JSON中的对象可以包含多个键值对,并且有数组结构,该语言正是一次实现过程内容的描述。
数组是由方括号括起来的一组值构成,如:
1
[3, 1, 4, 1, 5, 9, 2, 6]
表示对象
对象是一个无序的“‘名称/值’对”集合。一个对象以{左括号开始,}右括号结束。每个“名称”后跟一个:冒号;“‘名称/值’ 对”之间使用,逗号分隔。
1
{
"firstName"
:
"Brett"
,
"lastName"
:
"McLaughlin"
}
表示数组
和普通的 JS 数组一样,JSON 表示数组的方式也是使用方括号 []。
1
2
3
4
5
6
7
8
9
10
11
{
"people":[{
"firstName": "Brett",
"lastName":"McLaughlin"
},
{
"firstName":"Jason",
"lastName":"Hunter"
}]
}
字符串与C或者Java的字符串非常相似。字符串是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。
数字也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。
一些合法的JSON的实例:
1
{
"a"
: 1,
"b"
: [1, 2, 3]}
2
[1, 2,
"3"
, {
"a"
: 4}]
3
3.14
4
"plain_text"
下面给出一个我国省份城市Json实例:
{
"name": "中国",
"province": [{
"name": "黑龙江",
"cities": {
"city": ["哈尔滨", "大庆"]
}
}, {
"name": "广东",
"cities": {
"city": ["广州", "深圳", "珠海"]
}
}, {
"name": "台湾",
"cities": {
"city": ["台北", "高雄"]
}
}, {
"name": "新疆",
"cities": {
"city": ["乌鲁木齐"]
}
}]
}
其中, "province" 下是数组, 数组里包含各个省份对象, 省份下 "cities" 是城市对象,里面有对应的城市数组.
更通俗来说, 看见{ } 就去想对象, 看见[ ] 就去想数组.
三、JSON数据类型
在上面JSON基础中已有提到Json数据类型,下面进一步说明.
3.1 有效和无效的数据类型
在 JSON 中,值必须是以下数据类型之一:
- 字符串
- 数字
- 对象(JSON 对象)
- 数组
- 布尔
- Null
JSON 的值不可以是以下数据类型之一:
- 函数
- 日期
- undefined
3.2 JSON 字符串
JSON 中的字符串必须用双引号包围。
实例
{ "name":"Bill" }
3.3 JSON 数字
JSON 中的数字必须是整数或浮点数。
实例
{ "age":30 }
3.4 JSON 对象
JSON 中的值可以是对象。
实例
{ "employee":{ "name":"Bill Gates", "age":62, "city":"Seattle" } }
JSON 中作为值的对象必须遵守与 JSON 对象相同的规则。
3.5 JSON 数组
JSON 中的值可以是数组。
实例
{ "employees":[ "Bill", "Steve", "David" ] }
3.6 JSON 布尔
JSON 中的值可以是 true
/false
。
实例
{ "sale":true }
3.7 JSON null
JSON 中的值可以是 null
。
实例
{ "middlename":null }
四、JsonPath 的使用
参考至: JsonPath教程_koflance的博客-CSDN博客_jsonpath的用法
4.1 JsonPath 的介绍
通过名字可以知道,JsonPath和JSON文档有关系,JsonPath非常强大,通过JsonPath我们可以轻松的解析JSON,获取需要的内容.
4.2 JsonPath 语法
4.2.1 操作符
符号 | 描述 |
---|---|
$ | 查询的根节点对象,用于表示一个json数据,可以是数组或对象 |
@ | 过滤器断言(filter predicate)处理的当前节点对象,类似于java中的this字段 |
* | 通配符,可以表示一个名字或数字 |
.. | 可以理解为递归搜索,Deep scan. Available anywhere a name is required. |
.<name> | 表示一个子节点 |
[‘<name>’ (, ‘<name>’)] | 表示一个或多个子节点 |
[<number> (, <number>)] | 表示一个或多个数组下标 |
[start:end] | 数组片段,区间为[start,end),不包含end |
[?(<expression>)] | 过滤器表达式,表达式结果必须是boolean |
4.2.2 函数
名称 | 描述 | 输出 |
---|---|---|
min() | 获取数值类型数组的最小值 | Double |
max() | 获取数值类型数组的最大值 | Double |
avg() | 获取数值类型数组的平均值 | Double |
stddev() | 获取数值类型数组的标准差 | Double |
length() | 获取数值类型数组的长度 | Integer |
4.2.3 过滤器
过滤器是用于过滤数组的逻辑表达式,一个通常的表达式形如:[?(@.age > 18)],可以通过逻辑表达式&&或||组合多个过滤器表达式,例如[?(@.price < 10 && @.category == ‘fiction’)],字符串必须用单引号或双引号包围,例如[?(@.color == ‘blue’)] or [?(@.color == “blue”)]
操作符 | 描述 |
---|---|
== | 等于符号,但数字1不等于字符1(note that 1 is not equal to ‘1’) |
!= | 不等于符号 |
< | 小于符号 |
<= | 小于等于符号 |
> | 大于符号 |
>= | 大于等于符号 |
=~ | 判断是否符合正则表达式,例如[?(@.name =~ /foo.*?/i)] |
in | 所属符号,例如[?(@.size in [‘S’, ‘M’])] |
nin | 排除符号 |
size | size of left (array or string) should match right |
empty | 判空符号 |
示例:
String json = "
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}";
JsonPath表达式 | 结果 |
---|---|
$.store.book[*].author 或 $..author | [ “Nigel Rees”, “Evelyn Waugh”, “Herman Melville”, “J. R. R. Tolkien” ] |
/* 获取全部authors
["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
*/
// 1. $.store.book[*].author
List<String> authors1 = JsonPath.read(json, "$.store.book.[*].author");
// 2. $..author
List<String> authors2 = JsonPath.read(json, "$..author");
在使用时,强烈建议按照第一种语法进行使用!
处理JSON数据时,一定要层层筛选,避免出错,不要图省事!
JsonPath表达式 | 结果 |
---|---|
$.store.* 显示所有叶子节点值 | [[{”category” : “reference”,”author” : “Nigel Rees”,”title” : “Sayings of the Century”,”price” : 8.95},{”category” : “fiction”,”author” : “Evelyn Waugh”,”title” : “Sword of Honour”,”price” : 12.99},{”category” : “fiction”,”author” : “Herman Melville”,”title” : “Moby Dick”,”isbn” : “0-553-21311-3”,”price” : 8.99},{”category” : “fiction”,”author” : “J. R. R. Tolkien”,”title” : “The Lord of the Rings”,”isbn” : “0-395-19395-8”,”price” : 22.99}],{”color” : “red”,”price” : 19.95}] |
// [[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],{"color":"red","price":19.95}]
List<String> all = JsonPath.read(json, "$.store.*");
JsonPath表达式 | 结果 |
---|---|
$.store.book.[*].price $.store.book.[0].price | [8.95,12.99,8.99,22.99] [8.95] |
// [8.95,12.99,8.99,22.99]
List<String> prices = JsonPath.read(json, "$.store.book.[*].price");
// 8.95
double priceOfBook1 = JsonPath.read(json, "$.store.book.[0].price");
JsonPath表达式 | 结果 |
---|---|
$..book[0,1] | [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}] |
$..book[-2:] | [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}] |
// [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}]
List<String> books1 = JsonPath.read(json, "$..book[0, 1]");
List<String> books2 = JsonPath.read(json, "$..book[:2]");
//[{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
List<String> books3 = JsonPath.read(json, "$..book[-2:]");
//[{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
List<String> books4 = JsonPath.read(json, "$..book[2:]");
JSON数组跟Java、C数组一样,下标从0开始.
book[0, 1] 表示获取第一个,第二个书.
book[:2] 表示获取前两本书.
book[-2:] 表示获取最后两本书.
book[2:] 表示获取除前两本书外,剩余的书.
$..book[?(@.isbn)] $.store.book[?(@.price < 10)] $..book[?(@.price <= $[‘expensive’])] $..book[?(@.author =~ /.*REES/i)] | [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}] [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}] [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}] 所有符合正则表达式的书 |
//[{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
List<String> books5 = JsonPath.read(json, "$..book[?(@.isbn)]");
//[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
List<String> books6 = JsonPath.read(json, "$..book[?(@.price < 10)]");
//[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
List<String> books7 = JsonPath.read(json, "$..book[?(@.price < $['expensive'])]");
//[{”category” : “reference”,”author” : “Nigel Rees”,”title” : “Sayings of the Century”,”price” : 8.95}]
List<String> books8 = JsonPath.read(json, "$..book[?(@.author =~ /.*Rees/i)]");
$..book[?(@.isbn)] 表示获取所有含有“isbn” 的书
$.store.book[?(@.price < 10)] 表示获取所有价格低于10的书
$..book[?(@.price <= $[‘expensive’])] 表示获取所有价格低于expensive字段的值的书
$..book[?(@.author =~ /.*REES/i)] 表示获取所有作者名包含"REES" (不区分大小写) 的书
$.store.book.length() | 4 |
// 4
int len = JsonPath.read(json, "$.store.book.length()");
不使用函数,也可对JSON数据进行操作
List <Double> prices = JsonPath.read(json, "$.store.book.[*].price"); Double max = Double.valueOf(0.00); for (Double price : prices){ if (price > max){ max = price; } } // 22.99 System.out.println(max);
Tip: 贴两个在线资源 :
1. JSON在线格式化 : JSON在线视图查看器(Online JSON Viewer)
2. JsonPath在线编辑 : JSONPath Online Evaluator
4.3 JsonPath 常用用法
通常是直接使用静态方法API进行调用,例如:
String json = "...";
List<String> authors = JsonPath.read(json, "$.store.book[*].author");
但以上方式仅仅适用于解析一次json的情况,如果需要对同一个json解析多次,不建议使用,因为每次read都会重新解析一次json,针对此种情况,建议使用ReadContext、WriteContext,例如:
String json = "...";
ReadContext ctx = JsonPath.parse(json);
List<String> authorsOfBooksWithISBN = ctx.read("$.store.book[?(@.isbn)].author");
List<Map<String, Object>> expensiveBooks = JsonPath
.using(configuration)
.parse(json)
.read("$.store.book[?(@.price > 10)]", List.class);
4.4 JsonPath 返回值
通常read后的返回值会进行自动转型到指定的类型,对应明确定义definite的表达式,应指定其对应的类型,对于indefinite含糊表达式,例如包括..、?(<expression>)、[<number>, <number> (, <number>)],通常应该使用数组。如果需要转换成具体的类型,则需要通过configuration配置mappingprovider,如下:
String json = "{\"date_as_long\" : 1411455611975}";
//使用JsonSmartMappingProvider
Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);
//使用GsonMappingProvider
Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class);
4.5 MappingProvider SPI反序列化器
其中JsonSmartMappingProvider提供了如下基本数据类型的转换,此provider是默认设置的,在Configuration.defaultConfiguration()中返回的DefaultsImpl类,使用的就是JsonSmartMappingProvider。
DEFAULT.registerReader(Long.class, new LongReader());
DEFAULT.registerReader(long.class, new LongReader());
DEFAULT.registerReader(Integer.class, new IntegerReader());
DEFAULT.registerReader(int.class, new IntegerReader());
DEFAULT.registerReader(Double.class, new DoubleReader());
DEFAULT.registerReader(double.class, new DoubleReader());
DEFAULT.registerReader(Float.class, new FloatReader());
DEFAULT.registerReader(float.class, new FloatReader());
DEFAULT.registerReader(BigDecimal.class, new BigDecimalReader());
DEFAULT.registerReader(String.class, new StringReader());
DEFAULT.registerReader(Date.class, new DateReader());
切换Provider,如下:
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
@Override
public JsonProvider jsonProvider() {
return jsonProvider;
}
@Override
public MappingProvider mappingProvider() {
return mappingProvider;
}
@Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
}
});
4.6 Predicates过滤器断言
有三种方式创建过滤器断言。
4.6.1 Inline Predicates
即使用过滤器断言表达式?(<@expression>),例如:
List<Map<String, Object>> books = JsonPath.parse(json)
.read("$.store.book[?(@.price < 10)]");
4.6.2 Filter Predicates
使用Filter API。例如:
每一个语句的结果都以注释在上方给出
import static com.jayway.jsonpath.JsonPath.parse;
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
...
...
// [?(@['category'] == 'fiction' && @['price'] <= 10.0)]
Filter cheapFictionFilter = filter(
where("category").is("fiction").and("price").lte(10D)
);
List<Map<String, Object>> books =
parse(json).read("$.store.book[?]", cheapFictionFilter);
// [?(@['foo'] || @['bar'])]
Filter fooOrBar = filter(
where("foo").exists(true)).or(where("bar").exists(true)
);
// [?(@['foo'] && @['bar'])]
Filter fooAndBar = filter(
where("foo").exists(true)).and(where("bar").exists(true)
);
注意:
JsonPath表达式中必须要有断言占位符“?”,当有多个占位符时,会依据顺序进行替换。
多个filter之间还可以使用or或and。
4.6.3 Roll Your Own
自己实现Predicate接口。
ReadContext reader = JsonPath.parse(json);
Predicate booksWithISBN = new Predicate() {
@Override
public boolean apply(PredicateContext ctx) {
return ctx.item(Map.class).containsKey("isbn");
}
};
List<Map<String, Object>> books =
reader.read("$.store.book[?].isbn", List.class, booksWithISBN);
注意:在自定义过滤器中,context.item(Map.class) 这句话。这句中的Map.class是根据预定的结果类型定义的,如果返回的是String类型值,那就改为String.class
4.7返回检索到的Path路径列表
有时候需要返回当前JsonPath表达式所检索到的全部路径,可以如下使用:
// 首先设置 Configuration
Configuration conf = Configuration.builder()
.options(Option.AS_PATH_LIST).build();
// ["$['store']['book'][0]['author']",
// "$['store']['book'][1]['author']",
// "$['store']['book'][2]['author']",
// "$['store']['book'][3]['author']"]
List<String> pathList = using(conf).parse(json).read("$..author");
4.8 配置Options
4.8.1 DEFAULT_PATH_LEAF_TO_NULL
当检索不到时返回null对象,否则如果不配置这个,会直接抛出异常PathNotFoundException,例如:
[
{
"name" : "john",
"gender" : "male"
},
{
"name" : "ben"
}
]
Configuration conf = Configuration.defaultConfiguration();
//Works fine
String gender0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
//PathNotFoundException thrown
String gender1 = JsonPath.using(conf).parse(json).read("$[1]['gender']");
Configuration conf2 = conf.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
//Works fine
String gender0 = JsonPath.using(conf2).parse(json).read("$[0]['gender']");
//Works fine (null is returned)
String gender1 = JsonPath.using(conf2).parse(json).read("$[1]['gender']");
4.8.2 ALWAYS_RETURN_LIST
总是返回list,即便是一个确定的非list类型,也会被包装成list。
Configuration conf = Configuration.defaultConfiguration().addOptions(Option.ALWAYS_RETURN_LIST);
4.8.3 SUPPRESS_EXCEPTIONS
不抛出异常,需要判断如下:
ALWAYS_RETURN_LIST开启,则返回空list
ALWAYS_RETURN_LIST关闭,则返回null
4.8.4 AS_PATH_LIST
返回path
Configuration conf = Configuration.builder()
.options(Option.AS_PATH_LIST).build();
4.8.5 REQUIRE_PROPERTIES
如果设置,则不允许使用通配符,比如$[*].b,会抛出PathNotFoundException异常。
Configuration conf = Configuration.builder()
.options(Option.REQUIRE_PROPERTIES).build();
4.9 Cache SPI
每次read时都会获取cache,以提高速度,但默认情况下是不启用的。
@Override
public <T> T read(String path, Predicate... filters) {
notEmpty(path, "path can not be null or empty");
Cache cache = CacheProvider.getCache();
path = path.trim();
LinkedList filterStack = new LinkedList<Predicate>(asList(filters));
String cacheKey = Utils.concat(path, filterStack.toString());
JsonPath jsonPath = cache.get(cacheKey);
if(jsonPath != null){
return read(jsonPath);
} else {
jsonPath = compile(path, filters);
cache.put(cacheKey, jsonPath);
return read(jsonPath);
}
}
JsonPath 2.1.0提供新的spi,必须在使用前或抛出JsonPathException前配置。目前提供了两种实现:
com.jayway.jsonpath.spi.cache.NOOPCache (no cache)
com.jayway.jsonpath.spi.cache.LRUCache (default, thread safe)
如果想要自己实现,例如:
CacheProvider.setCache(new Cache() {
//Not thread safe simple cache
private Map<String, JsonPath> map = new HashMap<String, JsonPath>();
@Override
public JsonPath get(String key) {
return map.get(key);
}
@Override
public void put(String key, JsonPath jsonPath) {
map.put(key, jsonPath);
}
});
五、日常使用中一些坑
5.1 数据类型问题
处理JSON文件之间一定要先弄清楚数据类型
比如:json : { "soldNums" : [ { "soldNum" : 100 } , { "soldNum" : "1万+" } ] }
数组里的soldNum的数据类型既有整形也有字符串,出现不止一种数据类型的情况,有以下几种解决方案
1.统一用Object父类囊括
String json = "{\"soldNums\":[{\"soldNum\" : 100 },{\"soldNum\":\"1万+\"}]}"; List<Object> soldNums = JsonPath.read(json, "$.soldNums.[*].soldNum"); // 100 // 1万+ for (Object Num : soldNums){ String soldNum = String.valueOf(Num); // Object 转换 String System.out.println(soldNum); }
2. 使用JSONObject
里面是坑中坑
抛出异常 :
class java.util.LinkedHashMap cannot be cast to class net.minidev.json.JSONObject
List<JSONObject> soldNums = JsonPath.read(json, "$.soldNums.[*]"); for (JSONObject num : soldNums){ String soldNum = num.getAsString("soldNum"); System.out.println(soldNum); }
放到下一小节说
5.2 类型转换异常问题
书接上文抛出异常
一开始尝试强转, String.ValueOf(), (String), toString() ...... 都以失败告终
输出一下试试
List<JSONObject> soldNums = JsonPath.read(json, "$.soldNums.[*].soldNum"); System.out.println(soldNums);
输出结果 : [100,"1万+"]
查了好多资料,找到是反序列化的问题
资料1 : Jackson: java.util.LinkedHashMap cannot be cast to X | Baeldung
map是LinkedHashMap,得到的是map,而不是实体类。
贴代码:
List<JSONObject> jsons = new ArrayList<>(); jsons = (List<JSONObject>) JsonPath.read(json, "$.soldNums.[*]"); String s = JSON.toJSONString(jsons); jsons = JSON.parseArray(s, JSONObject.class); for (JSONObject num : jsons) { String soldNum = num.getAsString("soldNum"); System.out.println(soldNum); // 输出 soldNum 的类型 System.out.println(soldNum.getClass()); }
问题解决!
再加个例子: 用JSONObject会方便很多
(养成用JSONOBject来处理数据的习惯,方便)List<JSONObject > nums = (List<JSONObject>) JsonPath.read(json, "$..soldNums[?(@.soldNum == 100)]"); String s2 = JSON.toJSONString(nums); nums = JSON.parseArray(s2, JSONObject.class); for (JSONObject n : nums){ String sold = n.getAsString("soldNum"); System.out.println(sold); System.out.println(sold.getClass()); } // 输出结果 : 100 // class java.lang.String
5.3 JSONObject put()的顺序问题
/* Obj :{ "soldNum" : ... "tuanName" : ... "price" : ... "o_price" : ..." } put 进去的顺序和最终JSONObject的顺序不一致! */ JSONObject Obj = new JSONObject(); Obj.put("tuanName", tuanName); Obj.put("soldNum", soldNum); Obj.put("price", price); Obj.put("o_price", o_price); /* Obj :{ "tuanName" : ... "soldNum" : ... "price" : ... "o_price" : ..." } put 进去的顺序和最终JSONObject的顺序一致! */ JSONObject Obj = new JSONObject(new LinkedHashMap()); Obj.put("tuanName", tuanName); Obj.put("soldNum", soldNum); Obj.put("price", price); Obj.put("o_price", o_price);
原因:
JsonObject内部是默认用Hashmap来存储的,所以输出是按key的排序来的,如果要让JsonObject按固定顺序(put的顺序)排列,可以修改JsonObject的定义HashMap改为LinkedHashMap.
5.4 JSON嵌套格式问题
上周(7.18号),给的任务就是一个例子, 已经上传另一个文章 : https://blog.csdn.net/qq_51490751/article/details/125934371?spm=1001.2014.3001.5502
https://blog.csdn.net/qq_51490751/article/details/125934371?spm=1001.2014.3001.5502
最初思路比较乱, 要求的格式是这样
{ "tuan_list": [ { "tuanName": "【特惠指压】养气推拿", "soldNum": "1142", "price": "159", "o_price": "209" }, { "tuanName": "【本店爆款】禅【足浴 推拿】,免费WiFi,男女通用", "soldNum": "814", "price": "209", "o_price": "259" } ], "shopID": "H2WTGQ10qifYdc54" }
我操作后是....:
{ "tuan_list": [ { {"tuanName": "【特惠指压】养气推拿"}, {"soldNum": "1142"}, {"price": "159"}, {"o_price": "209"} }, { {"tuanName": "【本店爆款】禅【足浴 推拿】,免费WiFi,男女通用"}, {"soldNum": "814"}, {"price": "209"}, {"o_price": "259}" } ], "shopID": "H2WTGQ10qifYdc54" }
原本思路错了 : 每次获取一个属性, 就直接添加到JSONArray里面了, 导致每次都是加进去的是一个对象, 每个属性都是一个个独立的对象, 而不是一个对象里包含多个属性!
这是正确代码, 需要 两个 JSONObject 一个 JSONArray
for (JSONObject jsonObject : jsons){ String title = jsonObject.getAsString("tuanName"); String price = jsonObject.getAsString("price"); String marketPrice = jsonObject.getAsString("o_price"); String saleCount = jsonObject.getAsString("soldNum"); JSONObject obj = new JSONObject(new LinkedHashMap<>()); obj.put("title", title); obj.put("saleCount", saleCount); obj.put("price", price); obj.put("marketPrice", marketPrice); jsonArray.add(obj); } ansJson.put("shopTuan", jsonArray); ansJson.put("shopUuid", shopUuid);
问题解决!
5.5 文件写入问题
这里问题不是很大
// 用JSONObject 写入文件 JSONObject json = ......; FileUtils.writeStringToFile(new File("文件写入地址"), json.toJSONString() + "\n", "UTF-8", true);
几个注意点⚠️:
- 别自己在那sout, 慢, 容易错! 用JSONObject效率很高
- JSONObjet的toString()方法是转化成一行字符串,没有换行,记得加上 "\n" 换行符
- 在"UTF-8" 后的 true, 是继续写入的意思, 按append理解, 不设置为true写入会有问题,最后只写入一条.
六、总结
这两周主要都是跟JSON打交道, 磕磕绊绊, 出现很多错误. 对JSON格式和数据类型理解不清,导致代码非常脆弱,很容易抛出异常导致中断.集合类也要重新拿起来,好多方法非常好用,然而都忘了...要多复习.还有很重要一点,程序遇到问题多加断点调试,思路捋顺再解决问题😢.底层知识也要多看看,像文中的类型转换错误就卡了好久,要多查文档资料,不要躲避英文文档.
第一次写这么多的博客, 里面也有不少是借鉴的百度和CSDN, 看到例子多跑代码, 动手敲一敲会解决很多疑惑.
还有,写文档的时候态度要端正,不能糊弄了事,没搞懂以后全是坑,前两周已经被坑的快不行了...全是自己当时没弄懂的锅,前期逃得懒,都会利滚利之后在后面等我...(悲)
最后, 好好学习,天天向上,工作顺利.