解析Json

  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类型 JsonType Class 实际存储类型 注释
Array ARRAY JsonArray ArrayList  
Object OBJECT JsonObject HashMap  
String STRING JsonPrimitive java.lang.String  
Number   INTEGER JsonPrimitive java.lang.Long  
Number FLOAT JsonPrimitive java.lang.Double 不包括Infinity与NaN
Boolean BOOLEAN JsonPrimitive   java.lang.Boolean    对应常量true与false
Null NULL JsonPrimitive java.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),参数为要删除的子元素的下标。


你可能感兴趣的:(解析Json)