Json是一种基于js的轻量级数据交换格式,独立于特定的语言,其中对于信息的保存使用特殊的符合来实现不同的数据结构。可以构建两种基本数据结构:
1、对象
对象的概念类似于面向对象语言中的逻辑,采用 key/value的方式保存数据,同时使用{}包含来表示对象;如:
{name:'quzishen',company:'netease',department:{d1:'hangzhou',d2:'tech',d3:'dir'}}
可以看到,对象中可以继续包含子对象。
2、数组
数组也相同,使用我们习惯的[]包含,如:
[{name:'quzishen'},{name:'dinglei'}]
对于java等语言而言,这个json格式的数据其实就是一个字符串,如果使用最原始最古老的方式,你可以使用substring和split两个方法来不断切断解析出其中的字段和值,当然,我们希望的,是通过一个更为简便通用的方法,来完成一个转换,而不是上述的极有可能错误百出而无法重用的方法。
对于java语言解析json数据的开源jar有很多,我们使用json.jar来做一个解析工具类,首先下载jar包:
http://download.csdn.net/source/2835793
针对于json的数据格式,提供了连个对象类 : JSONObject 对象 和 JSONArray 数组。相关的配置内容,提供了一个JsonConfig 来保存相关的配置信息,比如在生成的json数据中,源对象中的哪些字段是不需要的等。
所以,我们只用这两个对象类,以及辅助的接口就可以做一个简单的json组装解析工具
/** * Json工具类 - 提供数据的Json化操作 * @author quzishen */ public class JsonUtil { /** * Json化对象 * @param obj 对象 * @param excludes 不包含的字段 * @return */ public static String getJsonFromObject(Object obj, String[] excludes) { if (obj != null) { return JSONObject.fromObject(obj, getExcludeConfig(excludes)) .toString(); } return "{}";//json语法中对象用大括号包含起来 } /** * Json化列表 * @param list 列表 * @param excludes 不包含的字段 * @return */ @SuppressWarnings("rawtypes") public static String getJsonFromList(List list, String[] excludes) { if (list != null) { return JSONArray.fromObject(list, getExcludeConfig(excludes)) .toString(); } return "[]";//json语法中数组用中括号包含起来 } /** * Json化值 * @param value 值 * @return */ public static String getJsonFromString(String value) { if (value == null) { return "{}"; } HashMap<String, String> map = new HashMap<String, String>(); map.put("value", value); return JSONObject.fromObject(map).toString(); } /** * 设置JsonConfig,不需要包含进去的字段 * @param excludes 不需要包含字段数组 * @return */ private static JsonConfig getExcludeConfig(String[] excludes) { JsonConfig config = new JsonConfig(); if (excludes != null && excludes.length > 0) config.setExcludes(excludes); return config; } /** * 从json中获取值 * @param jsonValue * @param valueKey * @return */ public static String getValueFromJsonObject(String jsonValue, String valueKey){ try { JSONObject jsonObject = JSONObject.fromObject(jsonValue); return jsonObject.getString(valueKey); } catch (Exception e){ return null; } } /** * 获取map类型数据 * @param jsonValue * @param keys * @return */ public static Map<String,Object> getMapFromJson(String jsonValue, List<String> keys){ if(null == keys || null == jsonValue){ return null; } Map<String,Object> result = new HashMap<String,Object>(); JSONObject jsonObject = JSONObject.fromObject(jsonValue); for (String key : keys){ Object value = jsonObject.get(key); result.put(key, value); } return result; } /** * 从数组json数据中,获取map列表 * @param arrayJsonStr * @param list * @return */ public static List<Map<String, Object>> getMapValueListFromArray(String arrayJsonStr, List<String> list){ List<Map<String, Object>> values = new ArrayList<Map<String, Object>>(); JSONArray ja = JSONArray.fromObject(arrayJsonStr); for (int i = 0 ;i < ja.size(); i++){ JSONObject o = ja.getJSONObject(i); Map<String, Object> resultMap = new HashMap<String,Object>(); for (String key : list){ resultMap.put(key,o.getString(key)); } values.add(resultMap); } return values; } /** * 从json对象中获取子对象 * @param jsonObject * @param valueKey * @return */ public static JSONObject getJsonObjectFromJsonObject(JSONObject jsonObject, String valueKey){ return jsonObject.getJSONObject(valueKey); }
测试代码:
public static void main(String[] args) { System.out.println(getJsonFromString("dddd")); List<String> list = new ArrayList<String>(); list.add("aaaa"); list.add("bbbb"); System.out.println(getJsonFromList(list,null));//new String[]{"name"} String js = "{name:'中国'}"; System.out.println(getValueFromJsonObject(js,"name")); MusicJsonDomain m = new MusicJsonDomain(); CircleMusicJsonDomain c = new CircleMusicJsonDomain(); m.setCircleMusic(c); System.out.println(getJsonFromObject(m, null)); String j = "{name:1,id:2,v:{i:1,j:2}}"; JSONObject jsonObject = JSONObject.fromObject(j); JSONObject t = jsonObject.getJSONObject("v"); System.out.println(jsonObject); System.out.println(t); List<String> keys = new ArrayList<String>(); keys.add("name"); keys.add("id"); keys.add("v"); Map<String,Object> result = getMapFromJson(j, keys); List<String> subkeys = new ArrayList<String>(); subkeys.add("i"); subkeys.add("j"); Object v = result.get("v"); Map<String,Object> subresult = getMapFromJson(v.toString(), subkeys); result.put("v", subresult); System.out.println(result); String mm = "[{n:'dd',i:1},{n:'aa',i:2}]"; list = new ArrayList<String>(); list.add("n"); list.add("i"); List<Map<String, Object>> values = getMapValueListFromArray(mm,list); System.out.println(values); }
其实最便利的,是针对于项目的领域模型,做特定的json格式和domain转换工具,最为方便。
通常而言,对于系统之间的交互,我们多数采用了ws或者esb来进行交互,这是作为服务器端java开发人员最常用的方式。对比而言,json的方式对于惯用了ws或者esb的开发人员来说,系统之间的通信方式的变更具有很大的逆转,需要一个适应过程,去琢磨其中的利弊。
首先,ws格式的通信,在soa架构中最为普遍的应用,通常的soa架构会将系统分级不同层次,比如核心系统,基础服务,通用产品,业务系统等,这样上下级关系明显理清之后,使用ws的通信,会清晰明了的多,但是也会有很大问题是,大多数的系统都是理不清的,而且架构层次是不明了的,彼此之间的调用是伴随业务发展不断增加扩展的,这样不管是xfire还是jax等,都会有一只直接的后果,就是系统中,外部领域对象会越来越多,尤其是那些最上层的应用系统,要不断的增加配置底层服务的返回对象等,除非是使用map的方式来保存数据。双方需要严格约定key和value的类型,并且保证各种类型序列化不出问题。
这样,服务端处理完成后,需要转换domain为map。从纯实现方式上讲,如果这个domain的字段太多,将是一个很容易出错而且很体力活的工作。
然后高级一点,使用esb,同步或者异步,在payload中负载的业务信息也同样存在相同的问题,要么是领域对象,要么是map的方式,同样的问题。曾经本人就遇到过,消息的发送者payload中的对象变更了jar包,导致接收到的消息不断报错类型转换错误。这其中的沟通成本太大,直接超出项目前期预算。尤其是系统架构很大的情况下,这种情况会很明显。
如果使用json,那么接口的返回业务值,只有一个string,而不会因为服务器端升级领域模型改变导致所有的客户端必须都升级。即便是服务器端升级,那么返回值中只要保证key还是那些key就可以,具体的客户端解析等不需要去关注,客户端不需要的字段,也可以直接忽略。
更为方便的 ,在大产品架构下,这种json的通信,在前段应用上,还有一个很大的优点,比如:
在大产品架构下,发展的方向很明显就是控件的通用化,js/css等效果的模式化,比如 投票控件/ 评论控件/ 上传控件 等等,这些控件都是直接可以配置到系统中通用的,比如使用freemarker可能只需要引用ftl然后加一个<@xxx/>宏就可以完成。其底层的调用等也是一些通用的ajax请求接口。
系统A -> B ,这样的调用关系下,B返回一个json格式封装一层,封装成一个js的触发器,也就是返回的结果是一个js串,可以直接执行调用js库中的一个通用方法,来完成一个操作。比如评论完成后自动加载最新的评论列表等。
这样对于调用者A而言,就不需要去做后续工作,如果这个工作由ws来完成,我们需要做的,首先得到返回值,然后将返回值渲染到视图中,然后刷新视图,自然的,这样并不是一个通用控件该有的模式。
从本项目中json作为数据交换格式的体验来看,这种方式下的工作带有很大的便利性,无论是开发阶段还是测试阶段,彼此之间解耦合各自分工互不影响,只要约定json数据中的结构即可。
参考资料:
http://www.json.org/json-zh.html