解析Json

来源:互联网 发布:日耳曼狂战士 知乎 编辑:程序博客网 时间:2024/06/04 01:00

  bantouyan-json库是用来解析与编码Json数据的Java库,该库按照Json标准RFC4627编写,能够实现字符串与Json实例的相互转换,可以读取Reader得到Json实例,或将Json实例写入到Writer,还能将Collection与Map对象转换为Json实例。

      RFC4627定义了Json的六种类型,分别是Array、Object、String、Number、Boolean(常量true与false)与Null(常量null)。在bantouyan-json库中,这些类型有枚举类型JsonType定义,其中Number被拆分为Integer与Float,非别表示整数与浮点数。这些类型的对应关系如下表所示:

RFC类型JsonTypeClass实际存储类型注释ArrayARRAYJsonArrayArrayList ObjectOBJECTJsonObjectHashMap StringSTRINGJsonPrimitivejava.lang.String Number  INTEGERJsonPrimitivejava.lang.Long NumberFLOATJsonPrimitivejava.lang.Double不包括Infinity与NaNBooleanBOOLEANJsonPrimitive  java.lang.Boolean   对应常量true与falseNullNULLJsonPrimitivejava.lang.String对应常量null

INTEGER使用类型long存储整数,FLOAT使用类型double存储浮点数,但是,Java浮点数常量正负INFINITY与NaN存储为STRING类型,因为按照RFC4627这三个常量的字面量不符合Number的定义。

      在bantouyan-json库中,JsonArray、JsonObject与JsonPrimitive有一个共同的超类Json,Json是一个抽象类,定义了这些类的一些共同特征,如Type、子元素的个数等,还提供了一些静态方法用来生成Json实例。

      在解析、处理、编码Json的过程中,总会发生这样或那样的错误,为此,定义了JsonException异常。如果在处理Json实例过程中产生了异常,如存取类型不正确,Json实例内出现了循环引用等,或在解析String、Reader或Java Map、 Collection为Json实例过程中产生了错误,都会抛出JsonException异常。JsonException异常都属于RuntimeException,不必要时可以不予捕获。

      在解析Collection或Map为Json实例的过程中,如果遇到普通的Java Class,bantouyan-json库就无法确定该如何解析这些普通类的对象。为此,json库中又定义了一个名为Jsonable的接口,该接口只有一个返回Json实例的方法generateJson()。故当解析Collection或Map时,如果遇到Jsonable的实例,就会调用所继承的generateJson()方法生成Json实例。

      除了接口Jsonable外,bantouyan-json库还定义了另外一个接口JsonParser,也用于将Collection或Map的转换。与Jsonable只负责把自己转换为Json实例不同,JsonParser负责把其他的Java对象转换为Json实例。当我们需要把非自己编写的Java类转换为Json实例时,JsonParser非常有用。

 

      特别警告:Json实例内部不允许出现循环引用,即Json实例内部,任何一个元素(包括顶层实例),都不能被其子元素或子元素的子元素引用,否则会引起一些方法出现异常或错误。因为从现实意义上讲,Json内部不会出现循环引用,但编写代码时可能有意无意的制造出有循环引用的实例。



解析Json——Json类的静态方法

要使用bantouyan-json库解析Json数据,可以使用类Json。类Json是JsonArray、JsonObject和JsonPrimitive的基类,它有四个静态方法用于解析Json数据,这四个方法分别是parseJsonText、parseJsonReader、parseJavaMap、parseJavaCollection,下面依次介绍。

 

一、parseJsonText

要将一个表示Json文本的字符串解析成一个Json实例,就要调用方法Json类的静态方法parseJsonText,示例代码如下:

Java代码  收藏代码
  1. import com.bantouyan.json.JsonObject;     
  2.     
  3. public class TestBtyJson     
  4. {     
  5.     public static void main(String[] args)     
  6.     {     
  7.         String jsonText = "{a: \"AA\", b: \"BB\", c: 23, d: true, e: null}";     
  8.         JsonObject jobj = (JsonObject)Json.parseJsonText(jsonText);     
  9.         System.out.println(jobj.getString("b"));     
  10.     }     
  11. }     
  12. //输出: BB   

      parseJsonText返回一个Json类实例,但在实际使用过程中,我们更常使用的类是JsonArray和JsonObject,你可以用instanceof操作符或者Json类的实例方法getType来确定返回实例的类型。通常情况下,我们知道所解析的字符串内的Json文本到底是表示JsonArray还是JsonObject,即我们知道返回的Json实例的类型,所以我们可以直接使用强制类型转换。

      如果我们传给方法parseJsonText的文本既不能表示成一个JsonArray,也不能表示成一个JsonObject,那么这个方法将抛出一个JsonException异常。这是一个runtimeException,没有必要时可以不予捕获,如果你不能保证所解析的Json文本格式正确,那么最好捕获这个异常并加以处理。捕获异常的示例代码如下:

Java代码  收藏代码
  1. import com.bantouyan.json.*;     
  2.     
  3. public class TestBtyJson     
  4. {     
  5.     public static void main(String[] args)     
  6.     {     
  7.         String jsonText = "{a \"AA\", b: \"BB\", c: 23, d: true, e: null}";     
  8.         JsonObject jobj = null;     
  9.         try    
  10.         {     
  11.             jobj = (JsonObject)Json.parseJsonText(jsonText);     
  12.         }      
  13.         catch (JsonException e)     
  14.         {     
  15.             System.out.println(e.getMessage());     
  16.         }     
  17.     }     
  18. }     
  19. //输出: Non-blank character found at position 3 before ':'.  

      这个例子中Json Object的第一个子元素的Name与Value之间少了分隔符“:”,导致抛出异常JsonException,异常的Message解释了出现异常的原因:在“:”之前发现了非空白字符。

 

 

二、parseJsonReader

      如果待解析的文本来自一个Reader对象,那么可以调用Json类的静态方法parseJsonReader,这个方法除了会抛出JsonException异常外,还会抛出IOException异常。示例代码如下:

Java代码  收藏代码
  1. import java.io.IOException;     
  2. import com.bantouyan.json.*;     
  3.     
  4. public class TestBtyJson     
  5. {     
  6.     public static void main(String[] args)     
  7.     {     
  8.         Reader reader = new ......     
  9.         JsonObject jobj = null;     
  10.         try    
  11.         {     
  12.             jobj = (JsonObject)Json.parseJsonReader(reader);     
  13.         }       
  14.         catch (IOException e)     
  15.         {     
  16.             e.printStackTrace();     
  17.         }     
  18.         catch (JsonException e)     
  19.         {     
  20.             System.out.println(e.getMessage());     
  21.         }     
  22.     }     
  23. }    

      parserJsonReader要求被解析的JsonReader内包含一个完整的Json字符串,不允许Json字符串前后有其他非空白内容,否则会抛出异常,而且,读取Reader导致的异常IOException必须在代码中予以处理。

 

 

三、parseJavaCollection

       如果要将一个Java Collection对象转换为一个JsonArray实例,就要调用方法parseJavaCollection,但要保证Collection对象内部没有循环引用并且所有的子元素都能解析,否则会抛出JsonException异常。示例代码如下:

Java代码  收藏代码
  1. import java.util.*;   
  2. import com.bantouyan.json.*;     
  3.     
  4. public class TestBtyJson     
  5. {     
  6.     public static void main(String[] args)     
  7.     {     
  8.         ArrayList<Object> list = new ArrayList<Object>();     
  9.         //list.add(list);     
  10.         //list.add(new Timer());     
  11.         JsonArray jary = null;     
  12.         try    
  13.         {     
  14.             jary = Json.parseJavaCollection(list);     
  15.         }      
  16.         catch (JsonException e)     
  17.         {     
  18.             System.out.println(e.getMessage());     
  19.         }     
  20.     }     
  21. }     
  22. //(第一行注释)输出:Java colloection is referenced again.     
  23. //(第二行注释)输出:Cannot parse value: java.util.Timer@60aeb0 to json instance.  

      例子中的ArrayList经过解析后将得到一个空的JsonArray实例。如果把代码中的第一行注释去掉,那么将抛出由循环引用导致的异常,如果把第二行注释去掉,那么将抛出由无法解析导致的异常。

 

 

四、parseJavaMap

      parseJavaMap与parseJavaCollection相似,只是把一个Java Map对象解析成JsonObject实例,同样要求Map对象内部没有循环引用并且所有的元素都能解析,否则会抛出JsonException异常。示例代码如下:

Java代码  收藏代码
  1. import java.util.*;   
  2. import com.bantouyan.json.*;     
  3.     
  4. public class TestBtyJson     
  5. {     
  6.     public static void main(String[] args)     
  7.     {     
  8.         HashMap<Object, Object> map = new HashMap<Object, Object>();     
  9.         //map.put("self", map);     
  10.         //map.put("a", new Timer());     
  11.         JsonObject jobj = null;     
  12.         try    
  13.         {     
  14.             jobj = Json.parseJavaMap(map);     
  15.         }      
  16.         catch (JsonException e)     
  17.         {     
  18.             System.out.println(e.getMessage());     
  19.         }     
  20.     }     
  21. }     
  22. //(第一行注释)输出:Java map is referenced again.     
  23. //(第二行注释)输出:Cannot parse value: java.util.Timer@60aeb0 to json instance.  

      这个例子与上一个类似,map对象解析后得到一个空的JsonObject实例,如果把代码中的第一行注释去掉,那么将抛出由循环引用导致的异常,如果把第二行注释去掉,那么将抛出由无法解析导致的异常。


五、JsonParser

      默认情况下,parseJavaCollection与parseJavaMap只能处理一些简单的类型,如String、Number、Boolean等,如果要处理普通的Java类,就要为这些类实现Jsonable接口。但是这有局限性,例如我们要处理的类不是我们自己编写的而是来自其他包,这时候再使用Jsonable就很不方便。为了解决这种不便,bantouyan-json库还提供了另外一个接口JsonParser,该接口用在parseJavaCollection与parseJavaMap的重载版本parseJavaCollection(Collection, Jsonparser)与parseJavaMap(Map, Jsonparser)中。

      Jsonparser有四个方法,canToName用于判断Java对象是否可以转换为JsonObject子元素的Name,changToName用于将Java对象转换为JsonObject子元素的Name,canToJson用于判断Java对象是否可以转换为Json实例,changeToJson用于将Java对象转换为Json实例。需要注意的是,只有通过canToJson(canToName)的Java对象,即返回true,才会在parseJavaCollection(Collection, Jsonparser)与parseJavaMap(Map, Jsonparser)中用changeToJson(changeToName)处理,如果通不过,即返回false,则按默认规则处理。

      下面是一个例子:

Java代码  收藏代码
  1. import com.bantouyan.json.*;  
  2.   
  3. public class StringParser implements JsonParser  
  4. {  
  5.     public boolean canToName(Object obj)  
  6.     {  
  7.         if(obj instanceof String)  
  8.             return ((String)obj).startsWith("*");  
  9.         else  
  10.             return false;  
  11.     }  
  12.   
  13.     public String changeToName(Object obj) throws JsonException  
  14.     {  
  15.         String str = (String)obj;  
  16.         return "Name_" + str.substring(1);  
  17.     }  
  18.       
  19.     public boolean canToJson(Object obj)  
  20.     {  
  21.         if(obj instanceof String)  
  22.             return ((String)obj).startsWith("#");  
  23.         else  
  24.             return false;  
  25.     }  
  26.   
  27.     public Json changeToJson(Object obj) throws JsonException  
  28.     {  
  29.         String str = (String)obj;  
  30.         return new JsonPrimitive("Value_" + str.substring(1));  
  31.     }  
  32. }  

 

Java代码  收藏代码
  1. import java.util.*;  
  2. import com.bantouyan.json.*;  
  3.   
  4. public class TestBtyJson  
  5. {  
  6.     public static void main(String[] args)  
  7.     {  
  8.         HashMap<Object, Object> map = new HashMap<Object, Object>();  
  9.         map.put("na""#va");  
  10.         map.put("*b""vb");  
  11.         ArrayList<Object> list = new ArrayList<Object>();  
  12.         list.add("eA");  
  13.         list.add("#eb");  
  14.         list.add(map);  
  15.           
  16.         JsonParser parser = new StringParser();  
  17.         JsonArray jaryA = Json.parseJavaCollection(list);  
  18.         System.out.println(jaryA);  
  19.         JsonArray jaryB = Json.parseJavaCollection(list, parser);  
  20.         System.out.println(jaryB);  
  21.     }  
  22. }  
  23. //输出:["eA","#eb",{"*b":"vb","na":"#va"}]  
  24. //输出:["eA","Value_eb",{"Name_b":"vb","na":"Value_va"}]  

 

      例子中的JsonParse对于以*开头的Name,会转换为以Name_开头, 对于以#开头的Value,会转换为以Value_开头的字符串型Json实例。



解析Json——Json类的实例方法


作为所有Json实例的基类Json定义了操作Json实例的通用方法,下面将一一介绍。

 

一、输出Json文本

      要把Json实例转换为字符串,可以调用Json类的方法generateJsonText,这个方法有两个重载版本,带参数的与不带参数的。带参数的generateJsonText(boolean)让你自己决定JsonObject子元素的Name部分是否用引号括起来,不带参数的版本相当于参数为false的情况,只是转换失败时仅返回一个空指针而不抛出异常。

      Json类还重写toString方法,toString方法等同于不带参数的generateJsonText()。

      在Servlet编程中,直接把Json文本输出到Response的Writer对象更方便。为此,Json类实现了outputToWriter方法,这个方法也有两个重载版本,分别是outputToWriter(PrintWriter, boolean)与outputToWriter(PrintWriter, boolean)。outputToWriter方法的第二个参数与generateJsonText方法的参数意义一致,第一个参数的区别是用PrinteWriter时不会抛出必须捕获的IOException,而用Writer时必须处理IOException。

 

二、Json实例的通用方法

      Json实例通用的方法有下面几个:

  •  
    •  
      • isEmtpy:判断Json实例是否包含子元素。
      • count:Json实例子元素的个数。
      • clear:清除Json实例所有的子元素。
      • getType:返回Json实例的类型。
      • existsCircle:Json实例内是否存在循环引用,如果存在会导致输出文本异常。

 

三、Json实例的相等性判断

      Json类重写了equals方法,只要两个Json实例所表示的数据一致(即类型一致、子元素的个数一致且对应相等,对于JsonPrimitive来讲是自身的值相等)就返回true,而不管在内存中的映像是否一致。

      Json类也重写了作为与equals配对使用的方法hashCode,只要equals方法返回true,hashCode肯定返回相同的值。

 

四、克隆Json实例

      Json类也重写了方法clone,能够实现Json实例的深度克隆,即无论如何修改被克隆出的Json实例(即使修改子元素的子元素),都不会影响原Json实例的值。



解析Json——操纵JsonObject

Json对象是Name Value对(即子元素)的无序集合,相当于一个Map对象。JsonObject类是bantouyan-json库对Json对象的抽象,提供操纵Json对象的各种方法。本文就介绍如何操纵JsonObject类。

 

一、创建JsonObject实例

      创建JsonObject实例有两类方法,一是利用超类Json的静态方法parseJsonText、parseJsonReader与parseJavaMap获取JsonObject实例,二是直接利用JsonObject类的构造方法创建JsonObject实例。

      根据传入的参数不同,parseJsonText返回一个JsonObject实例或JsonArray实例,利用parseJsonText方法的示例代码如下:

Java代码  收藏代码
  1. String jsonText = "{'name1': 'value1', 'name2': 'value2'}";  
  2. JsonObject jobj = (JsonObject)Json.parseJsonText(jsonText);   

parseJsonText返回的是一个Json类变量,所以要使用强制类型转换。

      parseJsonReader负责从Reader类型参数内读取Json文本流,然后转换为Json实例,与parseJsonText一样,返回值需要强制类型转换。

      方法parseJavaMap直接返回JsonObject变量,不用类型转换,示例代码如下:

Java代码  收藏代码
  1. HashMap<Object, Object> map = new HashMap<Object, Object>();  
  2. map.put("nameA""valueA");   
  3. map.put("nameB""valueB");   
  4. JsonObject jobj = Json.parseJavaMap(map);  

 

如果Map内有复杂的对象需要解析,可以用parseJavaMap的重载版本parseJavaMap(Map, JsonParser)来处理(JsonParser的使用参考解析Json——Json类的静态方法的第五部分)。

      JsonObject类的构造函数有四个重载版本:JsonObject()、JsonObject(int)、JsonObject(Map)与JsonObject(Map, JsonParser)。不带参数与带整型参数的重载版本都构造一个空的JsonObject实例,所不同的是带整型参数的重载版本能够指定JsonObject初始容量的大小,以避免不必要的重新分配内存。重载版本JsonObject(Map)与JsonObject(Map, JsonParser)的使用类似于Json类的静态方法parseJavaMap。

 

二、给JsonObject添加子元素

      给JsonObject实例添加子元素调用方法add或addAll。bantouyan-json库规定,方法add与addAll都不能添加Name为null的子元素,也不能添加与已有子元素Name相同的子元素,否则会抛出异常。

      方法add有七种重载版本,方法addAll有三种重载版本,分别使用于不同的情况。

 

三、变更JsonObject子元素的Value

      要改变JsonObject子元素的Value可以调用方法set与setAll。这两个方法都忽略Name为null的子元素,如果存在Name相同的子元素,则更改这个子元素的Value,否则添加一个新的子元素。

      方法set也有七种重载版本,addAll有三种重载版本,分别适用于不同的情况。

 

四、获取与检测JsonObject子元素

      JsonObject的每个子元素的Value都是一个Json实例,可以用方法get(String)获取这个实例。至于这个实例的类型,除可以调用方法getType()获得外,还可以通过JsonObject对方法getType的重载版本getType(String)获取(String为子元素的Name)。

      利用方法getString(String)可以获取指定Name的子元素Value的字符串值,如果子元素的Value是JsonPrimitive实例,则返回这个实例值对应的字符串(不带引号与转义符),否则返回对应的标准Json文本。

      如果想获取子元素的Value所对应的boolean、double、long、JsonArray与JsonObject类型的值,则可以分别调用方法getBoolean(String)、getDouble(String)、getLong(String)、getJsonArray(String)与getJsonObject(String)。与getString方法不一样的是当子元素的Value无法转换为相应的类型时会抛出异常。方法canToBoolean(String)、canToDouble(String)、canToLong(String)、canToJsonArray(String)与canToJsonObject(String)可以检测是否可以转换为对应的类型。

      以特定的类型获取子元素的Value时,方法canToXXX返回true并不表明子元素的Value就是所测试的类型。根据bantouyan-json库设计,如果子元素的Value的类型是INTEGER,则可以得到对应的double类型的值,如果类型时String,对于部分Value,可以得到对应的boolean、long、double类型的值。 

 

五、确定JsonObject子元素是否存在

      在获取子元素前可能无法确定JsonObject是否包含指定Name的子元素,要确定子元素存在,请调用方法containsName(String)。

 

六、删除JsonObject子元素

      删除JsonObject的子元素调用方法remove(String),参数为要删除的子元素的Name。

 

 七、获取JsonObject子元素相关的集合

      与Map一样,JsonObject也是由无序的Name Value对构成,为此,JsonObject实现了三个方法用于获取子元素相关的集合。

      entrySet(),返回由子元素的Name Value对构成的集合。

      nameSet(),返回由子元素的Name构成的集合。

      values(),返回由子元素的Value构成的集合。



解析Json——操纵JsonArray

Json数组是子元素的有序集合,每个子元素都有一个下标,可以根据下标操纵Json数组的子元素。类JsonArray是bantouyan-json库对Json数组的抽象,提供操纵Json数组的各种方法。本文就介绍如何操纵JsonArray。

 

一、创建JsonArray实例

      创建JsonArray实例有两类方法,一是利用超类Json的静态方法parseJsonText、parseJsonReader与parseJavaCollection获取JsonArray实例,二是直接利用JsonArray类的构造方法创建JsonArray实例。

      根据传入的参数不同,parseJsonText返回一个JsonObject实例或JsonArray实例,利用parseJsonText方法的示例代码如下:

Java代码  收藏代码
  1. String jsonText = "['value1', 'value2', true, null]";   
  2. JsonArray jary = (JsonArray)Json.parseJsonText(jsonText);   

parseJsonText返回的是一个Json类变量,所以要使用强制类型转换。

      parseJsonReader负责从Reader类型参数内读取Json文本流,然后转换为Json实例,与parseJsonText一样,返回值需要强制类型转换。


      方法parseJavaCollection直接返回JsonArray变量,不用类型转换,示例代码如下:

Java代码  收藏代码
  1. ArrayList<Object> collection = new ArrayList<Object>();   
  2. collection.add("value1");   
  3. collection.add(true);   
  4. collection.add(30);   
  5. collection.add(null);   
  6. JsonArray jary = Json.parseJavaCollection(collection);  

如果Collection内有复杂的对象需要解析,可以用parseJavaCollection的重载版本parseJavaCollection(Collection, JsonParser)来处理(JsonParser的使用参考解析Json——Json类的静态方法的第五部分)。

 

      JsonArray类的构造函数有四个重载版本:JsonArray()、JsonArray(int)、JsonArray(Collection)与JsonArray(Collection, JsonParser)。不带参数与带整型参数的重载版本都构造一个空的JsonArray实例,所不同的是带整型参数的重载版本能够指定JsonArray初始容量的大小,以避免不必要的重新分配内存。重载版本JsonArray(Collection)与JsonArray(Collection, JsonParser)的使用类似于Json类的静态方法parseJavaCollection。

 

二、给JsonArray添加子元素

      JsonArray是子元素的有序集合,所以给JsonArray添加子元素应该指明子元素的位置,方法insert、insertAll、append、appendAll都可以添加子元素到JsonArray,不同的是方法insert与insertAll可以在任意位置添加子元素,方法append与appendAll只能在JsonArray的末尾追加子元素。

      方法insert与append都有七种重载版本,方法insertAll与appendAll都有三种重载版本,分别适用于不同的情况。

 

三、修改JsonArray的子元素

      方法set用来修改JsonArray子元素的值,有七种重载版本,每种版本的第一个参数都是被修改的子元素的下标。

 

四、获取与检测JsonArray子元素

      JsonArray的每个子元素都是一个Json实例,可以用方法get(int)获取这个实例。至于这个实例的类型,除可以调用方法getType()获得外,还可以通过JsonArray对方法getType的重载版本getType(int)获取(参数为子元素的下标)。

      利用方法getString(int)可以获取指定下标子元素的字符串值,如果子元素是JsonPrimitive实例,则返回这个实例值对应的字符串(不带引号与转义符),否则返回对应的标准Json文本。

      如果想获取子元素所对应的boolean、double、long、JsonArray与JsonObject类型的值,则可以分别调用方法getBoolean(int)、getDouble(int)、getLong(int)、getJsonArray(int)与getJsonObject(int)。与getString方法不一样的是当子元素无法转换为相应的类型时会抛出异常。方法canToBoolean(int)、canToDouble(int)、canToLong(int)、canToJsonArray(int)与canToJsonObject(int)可以检测是否可以转换为对应的类型。

      以特定的类型获取子元素时,方法canToXXX返回true并不表明子元素就是所测试的类型。根据bantouyan-json库的设计,如果子元素的类型是INTEGER,则可以得到对应的double类型的值,如果类型时String,对于部分情况,可以得到对应的boolean、long或double类型的值。

 

五、删除JsonArray子元素

      删除JsonArray的子元素调用方法remove(int),参数为要删除的子元素的下标。


原创粉丝点击