在Java中操作Json对象时,经常遇到以下几种情况:
Json字符串与Json对象的转换
Json对象与Java对象的转换
以下使用json-lib第3方工具进行讲解
其中Java对象→Json对象→Json字符串用的较多,其操作也较为简单,
1.Java对象→Json对象
private static void JavaObj2JsonObj() {
//将Java对象转换成Json对象,转换后的类型为JSONObject
JSONObject jsonObj = JSONObject.fromObject(javaObj);
//对象Map对象,也使用JSONObject的putAll方法将map内的键值对转换为Json对象中的属性-值
HashMap<String,Object> map = new HashMap<String, Object>();
map.put("tel", "13566662222");
jsonObj.putAll(map);
//put方法可设置一个属性值
jsonObj.put("key","123");
//element方法返回Json对象本身,可以进行串联调用
jsonObj.element("name", "java").element("length", 4);
List<Object> list = new ArrayList<Object>();
list.add(javaObj);
//声明一个数组对象
JSONArray jsonAry = new JSONArray();
jsonAry.addAll(list);
jsonAry.add(javaObj);
System.out.println(jsonObj);
System.out.println(jsonAry);
}
输出结果如下:
{"born":{"time":1572407278199,"minutes":47,"seconds":58,"hours":11,"month":9,"year":119,"timezoneOffset":-480,"day":3,"date":30},"tel":"13566662222","name":"java","age":11,"length":4,"key":"123"}
[{"born":{"time":1572407278199,"minutes":47,"seconds":58,"hours":11,"month":9,"timezoneOffset":-480,"year":119,"day":3,"date":30},"age":11,"name":"john"},{"born":{"time":1572407278199,"minutes":47,"seconds":58,"hours":11,"month":9,"timezoneOffset":-480,"year":119,"day":3,"date":30},"age":11,"name":"john"}]
1.1日期对象的处理
可以看到,在Java对象到Json对象转换时,相对较为简单,但对日期这类较特殊的对象进行转换时,默认会转换成一个JSONObject对象,在转换为Json对象时会按一般对象进行处理,转换后的内容可读性较差,如何将日期对象转换为可读性强的日期字符串呢?这就要用到JsonConfig类了,使用JsonConfig类可以指定转换规则,如果想将日期对象转换为yyyy-MM-dd HH:mm:ss.SSS的字符串,就可以使用以下的代码:
private static void JavaObj2JsonObjFmtDate() {
JsonConfig cfg = new JsonConfig();
cfg.registerJsonValueProcessor(Date.class, new JsonValueProcessor() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public Object processObjectValue(String propt, Object val, JsonConfig cfg) {
return processArrayValue(val, cfg);
}
public Object processArrayValue(Object val, JsonConfig arg1) {
if(val instanceof Date){
return sdf.format((Date)val);
}
return null;
}
});
JSONObject json = JSONObject.fromObject(javaObj);
System.out.println("default json string:");
System.out.println(json.toString());
System.out.println("with JsonConfig which register Date processor:");
json = JSONObject.fromObject(javaObj, cfg);
System.out.println(json.toString());
}
使用这段代码后,日期对象将转换为字符串格式,可读性大大提高,输出结果如下所示:
default json string:
{"born":{"time":1572406936364,"minutes":42,"seconds":16,"hours":11,"month":9,"year":119,"timezoneOffset":-480,"day":3,"date":30},"name":"john","age":11}
with JsonConfig which register Date processor:
{"born":"2019-10-30 11:42:16.364","name":"john","age":11}
1.2Java对象属性过滤
如果想在转换时,指定某些属性不转换到Json对象时该怎么办呢?看代码:
private static void JavaObj2JsonObjFilterPropt() {
JsonConfig cfg = new JsonConfig();
cfg.setJsonPropertyFilter(new PropertyFilter() {
public boolean apply(Object obj, String propt, Object val) {
if("born".equals(propt)){
return true;//返回true时表示此字段不转换至Json对象中
}
return false;
}
});
JSONObject json = JSONObject.fromObject(javaObj);
System.out.println("default json string:");
System.out.println(json.toString());
System.out.println("with JsonConfig which filter born propt:");
json = JSONObject.fromObject(javaObj, cfg);
System.out.println(json.toString());
}
PropertyFilter接口中仅有一个apply方法,此方法有3个参数,可以根据需要对指定的字段进行过滤,当返回true时表示要过滤此字段
输出结果如下:
default json string:
{"born":{"time":1572407140061,"minutes":45,"seconds":40,"hours":11,"month":9,"year":119,"timezoneOffset":-480,"day":3,"date":30},"name":"john","age":11}
with JsonConfig which filter born propt:
{"name":"john","age":11}
2.Json对象到Json字符串
Json对象到Json字符串最简单,默认使用toString方法即可返回字符串,如果要使用缩进模式输出,则在toString中增加缩进数量的参数,直接看代码吧:
private static void JsonObj2JsonStr() {
JSONObject json = JSONObject.fromObject(javaObj);
System.out.println(json.toString());
//每层缩进2个空白字符
System.out.println(json.toString(2));
//每层缩进2个空白字符,顶层缩进1个空白字符
System.out.println(json.toString(2, 1));
}
输出结果如下:
{"born":{"time":1572413765947,"minutes":36,"seconds":5,"hours":13,"month":9,"year":119,"timezoneOffset":-480,"day":3,"date":30},"name":"john","age":11}
{
"born": {
"time": 1572413765947,
"minutes": 36,
"seconds": 5,
"hours": 13,
"month": 9,
"year": 119,
"timezoneOffset": -480,
"day": 3,
"date": 30
},
"name": "john",
"age": 11
}
{
"born": {
"time": 1572413765947,
"minutes": 36,
"seconds": 5,
"hours": 13,
"month": 9,
"year": 119,
"timezoneOffset": -480,
"day": 3,
"date": 30
},
"name": "john",
"age": 11
}
压缩存储空间时不要使用缩进,做为展示内容时可设置缩进长度为2,此时显示效果最好,顶层缩进在一般情况下用处不大。
3.Json字符串到Json对象
Json字符串到Json对象比较简单,只需要调用JSONObject和JSONArray对象的静态方法 fromObject,传入Json字符串,即可得到Json对象
private static void JsonStr2JsonObj() {
JSONObject json = JSONObject.fromObject(javaObj);
String jsonStr = json.toString();
json = JSONObject.fromObject(jsonStr);
System.out.println(json.toString());
JSONArray jsonAry = new JSONArray();
jsonAry.add(javaObj);
jsonStr = jsonAry.toString();
jsonAry = JSONArray.fromObject(jsonStr);
System.out.println(jsonAry.toString());
}
输出结果如下:
{"born":{"time":1572414590736,"minutes":49,"seconds":50,"hours":13,"month":9,"year":119,"timezoneOffset":-480,"day":3,"date":30},"age":11,"name":"john"}
[{"born":{"time":1572414590736,"minutes":49,"seconds":50,"hours":13,"month":9,"timezoneOffset":-480,"year":119,"day":3,"date":30},"name":"john","age":11}]
3.1Json对象属性过滤
转换时如果不需要某些属性,可以使用JsonConfig对象进行过滤
private static void JsonStr2JsonObjFilterPropt() {
JsonConfig cfg = new JsonConfig();
cfg.setJsonPropertyFilter(new PropertyFilter() {
public boolean apply(Object obj, String propt, Object val) {
if("born".equals(propt)){
return true;//返回true时表示此字段不转换至Json对象中
}
return false;
}
});
JSONObject json = JSONObject.fromObject(javaObj);
String jsonStr = json.toString();
System.out.println(jsonStr);
json = JSONObject.fromObject(jsonStr, cfg);
System.out.println(json.toString());
}
输出结果如下:
{"born":{"time":1572414590736,"minutes":49,"seconds":50,"hours":13,"month":9,"year":119,"timezoneOffset":-480,"day":3,"date":30},"name":"john","age":11}
{"age":11,"name":"john"}
4.Json对象到Java对象
JSONObject 对象的静态方法toBean可以将一个Json对象转换为Java对象
private static void JsonObj2JavaObj() {
JSONObject json = JSONObject.fromObject(javaObj);
Object obj = JSONObject.toBean(json);
System.out.println(obj);
}
调用后输出结果如下:
net.sf.ezmorph.bean.MorphDynaBean@340d1fa5[
{born=net.sf.ezmorph.bean.MorphDynaBean@4998a455[
{time=1572416609829, minutes=23, seconds=29, hours=14, month=9, year=119, timezoneOffset=-480, day=3, date=30}
], name=john, age=11}
]
4.1指定转换的Java类
发现通过JsonNode类的对象转换成Json对象后,再用toBean方法转换为Java对象时,变成了MorphDynaBean,如何将转换后的对象仍变成JsonNode呢?还是要使用JsonConfig对象
private static void JsonObj2JavaObjSetRootClass() {
JSONObject json = JSONObject.fromObject(javaObj);
System.out.println(json.toString());
JsonConfig cfg = new JsonConfig();
cfg.setRootClass(JsonNode.class);
Object obj = JSONObject.toBean(json, cfg);
System.out.println(obj);
}
此时输出结果如下:
{"born":{"time":1572417309515,"minutes":35,"seconds":9,"hours":14,"month":9,"year":119,"timezoneOffset":-480,"day":3,"date":30},"name":"john","age":11}
14:35:09 WARN [net.sf.json.JSONObject] - Property 'timezoneOffset' has no write method. SKIPPED.
14:35:09 WARN [net.sf.json.JSONObject] - Property 'day' has no write method. SKIPPED.
net.sf.json.JsonNode@54bb7759
4.2转换时过滤属性
此时输出的类是正确的,但出现了WARN的警告,提示timezoneOffset和day属性没有写方法,如何消除这些警告呢?可以在JsonConfig中增加属性过滤
private static void JsonObj2JavaObjFilterPropt() {
JSONObject json = JSONObject.fromObject(javaObj);
System.out.println(json.toString());
JsonConfig cfg = new JsonConfig();
cfg.setRootClass(JsonNode.class);
cfg.setJavaPropertyFilter(new PropertyFilter() {
public boolean apply(Object obj, String propt, Object val) {
if("timezoneOffset".equals(propt) || "day".equals(propt)){
return true;//返回true时表示此字段不转换至Json对象中
}
return false;
}
});
Object obj = JSONObject.toBean(json, cfg);
System.out.println(obj);
}
输出结果如下:
{"born":{"time":1572417839524,"minutes":43,"seconds":59,"hours":14,"month":9,"year":119,"timezoneOffset":-480,"day":3,"date":30},"name":"john","age":11}
net.sf.json.JsonNode@1d10c424
4.3日期字符串转换为日期对象
如果Json对象中的日期是格式化日期字符串,此时在转换为Java对象时要进行特殊的处理,要注册日期转换对象DateMorpher,此行代码只需在toBean方法执行前执行即可,放在静态代码块执行也可以。
private static void JsonObj2JavaObjFmtDate() {
JsonConfig cfg = new JsonConfig();
cfg.registerJsonValueProcessor(Date.class, new JsonValueProcessor() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public Object processObjectValue(String propt, Object val, JsonConfig cfg) {
return processArrayValue(val, cfg);
}
public Object processArrayValue(Object val, JsonConfig arg1) {
if(val instanceof Date){
return sdf.format((Date)val);
}
return null;
}
});
JSONObject json = JSONObject.fromObject(javaObj, cfg);
System.out.println(json.toString());
//此行非常重要,如果没有此行代码,则日期字符串不能被正确转换为Java的日期对象,可能为空或指定为当前时间
JSONUtils.getMorpherRegistry().registerMorpher(new DateMorpher(new String[] {"yyyy-MM-dd", "yyyy-MM-dd HH:mm", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss.SSS"}));
cfg = new JsonConfig();
cfg.setRootClass(JsonNode.class);
Object obj = JSONObject.toBean(json, cfg);
System.out.println(obj);
}
输出结果如下:
{"born":"2019-10-30 14:50:23.312","name":"john","age":11}
net.sf.json.JsonNode@32b0bad7
4.4指定转换时各属性对应的类
如果要转换的Java类有多层嵌套,如何指定类模板呢?
private static void JsonObj2JavaObjSetProptClass() {
JsonMainNode jmn = new JsonMainNode();
jmn.setName("mainNode");
jmn.setJsonNode(javaObj);
jmn.setChildren(new ArrayList<JsonNode>());
jmn.getChildren().add(javaObj);
JSONObject json = JSONObject.fromObject(jmn);
System.out.println(json.toString());
JsonConfig cfg = new JsonConfig();
cfg.setRootClass(JsonMainNode.class);
Object obj = JSONObject.toBean(json, cfg);
System.out.println(obj);
System.out.println(((JsonMainNode)obj).getJsonNode());
System.out.println(((JsonMainNode)obj).getChildren());
Map<String, Class> cmap = new HashMap<String, Class>();
cmap.put("children", JsonNode.class);
cfg.setClassMap(cmap);
obj = JSONObject.toBean(json, cfg);
System.out.println(obj);
System.out.println(((JsonMainNode)obj).getJsonNode());
System.out.println(((JsonMainNode)obj).getChildren());
}
输出结果如下:
{"name":"mainNode","jsonNode":{"born":{"time":1572419153235,"minutes":5,"seconds":53,"hours":15,"month":9,"year":119,"timezoneOffset":-480,"day":3,"date":30},"name":"john","age":11},"children":[{"born":{"time":1572419153235,"minutes":5,"seconds":53,"hours":15,"month":9,"timezoneOffset":-480,"year":119,"day":3,"date":30},"age":11,"name":"john"}]}
15:05:53 WARN [net.sf.json.JSONObject] - Property 'timezoneOffset' has no write method. SKIPPED.
15:05:53 WARN [net.sf.json.JSONObject] - Property 'day' has no write method. SKIPPED.
net.sf.json.JsonMainNode@3f57fb52
net.sf.json.JsonNode@2934e4fb
[net.sf.ezmorph.bean.MorphDynaBean@4fa52fdf[
{born=net.sf.ezmorph.bean.MorphDynaBean@65ea0252[
{time=1572419153235, minutes=5, seconds=53, hours=15, month=9, timezoneOffset=-480, year=119, day=3, date=30}
], age=11, name=john}
]]
15:05:53 WARN [net.sf.json.JSONObject] - Property 'timezoneOffset' has no write method. SKIPPED.
15:05:53 WARN [net.sf.json.JSONObject] - Property 'day' has no write method. SKIPPED.
15:05:53 WARN [net.sf.json.JSONObject] - Property 'timezoneOffset' has no write method. SKIPPED.
15:05:53 WARN [net.sf.json.JSONObject] - Property 'day' has no write method. SKIPPED.
net.sf.json.JsonMainNode@528acf6e
net.sf.json.JsonNode@17386918
[net.sf.json.JsonNode@787bb290]
可以看出,如果字段类型为强类型时,转换时可以自动转换为原类型,但如果属性为List时,默认转换时会将List中的对象转换为MorphDynaBean的对象,此时可以使用setClassMap为属性指定类型,这样就可以得到我们希望的结果。
5.在线API链接
http://tool.oschina.net/apidocs/apidoc?api=json-lib2.4
主要的配置类JsonConfig中,查看API时需注意配置项是Java→JSON,还是JSON→Java