原文地址:http://www.studytrails.com/java/json/java-google-json-parse-json-token-by-token.jsp
在上一节教程当中,我们看到如何将json转换成java对象,这节中我们会涉及到自定义解析json.这种方式看起来似乎麻烦,但是当你需要进行自定义解析的时候,这种方式就会显得灵活方便.使用的方式:
通过使用JsonReader来读取json字符串来获取标记流.一个对象或者一个数组,也就是”{“,”[“,是一个标记.
以下为一个demo:
package com.studytrails.json.gson;
import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.io.IOUtils;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
public class ParseTokenExample7
{
public static void main(String[] args) throws MalformedURLException, IOException
{
String url = "http://freemusicarchive.org/api/get/albums.json?api_key=60BLHNQCAOUFPIBZ&limit=1";
String json = IOUtils.toString(new URL(url));
// use the reader to read the json to a stream of tokens
JsonReader reader = new JsonReader(new StringReader(json));
// we call the handle object method to handle the full json object. This
// implies that the first token in JsonToken.BEGIN_OBJECT, which is
// always true.
handleObject(reader);
}
/**
* Handle an Object. Consume the first token which is BEGIN_OBJECT. Within
* the Object there could be array or non array tokens. We write handler
* methods for both. Noe the peek() method. It is used to find out the type
* of the next token without actually consuming it.
*
* @param reader
* @throws IOException
*/
private static void handleObject(JsonReader reader) throws IOException
{
reader.beginObject();
while (reader.hasNext()) {
JsonToken token = reader.peek();
if (token.equals(JsonToken.BEGIN_ARRAY))
handleArray(reader);
else if (token.equals(JsonToken.END_OBJECT)) {
reader.endObject();
return;
} else
handleNonArrayToken(reader, token);
}
}
/**
* Handle a json array. The first token would be JsonToken.BEGIN_ARRAY.
* Arrays may contain objects or primitives.
*
* @param reader
* @throws IOException
*/
public static void handleArray(JsonReader reader) throws IOException
{
reader.beginArray();
while (true) {
JsonToken token = reader.peek();
if (token.equals(JsonToken.END_ARRAY)) {
reader.endArray();
break;
} else if (token.equals(JsonToken.BEGIN_OBJECT)) {
handleObject(reader);
} else if (token.equals(JsonToken.END_OBJECT)) {
reader.endObject();
} else
handleNonArrayToken(reader, token);
}
}
/**
* Handle non array non object tokens
* @param reader
* @param token
* @throws IOException
*/
public static void handleNonArrayToken(JsonReader reader, JsonToken token) throws IOException
{
if (token.equals(JsonToken.NAME))
System.out.println(reader.nextName());
else if (token.equals(JsonToken.STRING))
System.out.println(reader.nextString());
else if (token.equals(JsonToken.NUMBER))
System.out.println(reader.nextDouble());
else
reader.skipValue();
}
}
可以看到这个demo中的json跟上一节的是一样的结构.
上面的教程中比较生涩(大神请自动忽略),具体可以看到JsonReader的api说明(在这里我翻译出来):
JsonReader这个类可以把Json解析成一个标记流,这个标记流中包含字面的值(string,number,boolean,null)以及对象和数组的开始,结束符号.也就是{}和[].这些标记的顺序跟在json中出现的顺序是一致的.在Json object中,键值对是一个标记.
解析的大概流程:
首先创建一个JsonReader对象,然后为已知的json结构创建处理的方法.分为object和array两种类型.
- 在array类型的处理方法中,首先需要调用beginArray()方法来消费掉”[“这个标记,然后创建一个循环,直到hasNext()方法返回是false,结束这个循环.在循环中,我们就可以读取所有的键值对,最后,调用endArray()方法消费掉”]”标记.
- 同样的,在object类型的处理中,首先调用begingObject()来消费掉”{“,以及最后调用endObject()来消费掉”}”,在两者之间进行一个读取键值对的循环,知道hasNext()返回fasle;
遇到对应的类型的时候,调用相应的方法处理即可.当遇到未知的类型的时候,严格的解析是要抛出一个异常的.而宽松的解析则可以通过skipValue()来跳过未知类型.
如果一个值是null的时候,可以使用peek()方法来检查是否为空,空标记可以用nextNull()或者skipValue()方法来消费掉.
以下是一个demo:
要解析的json:
[
{
"id": 912345678901,
"text": "How do I read a JSON stream in Java?",
"geo": null,
"user": {
"name": "json_newb",
"followers_count": 41
}
},
{
"id": 912345678902,
"text": "@json_newb just use JsonReader!",
"geo": [50.454722, -104.606667],
"user": {
"name": "jesse",
"followers_count": 2
}
}
]
代码:
public List<Message> readJsonStream(InputStream in) throws IOException {
JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
try {
return readMessagesArray(reader);
} finally {
reader.close();
}
}
public List<Message> readMessagesArray(JsonReader reader) throws IOException {
List<Message> messages = new ArrayList<Message>();
reader.beginArray();
while (reader.hasNext()) {
messages.add(readMessage(reader));
}
reader.endArray();
return messages;
}
public Message readMessage(JsonReader reader) throws IOException {
long id = -1;
String text = null;
User user = null;
List<Double> geo = null;
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("id")) {
id = reader.nextLong();
} else if (name.equals("text")) {
text = reader.nextString();
} else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
geo = readDoublesArray(reader);
} else if (name.equals("user")) {
user = readUser(reader);
} else {
reader.skipValue();
}
}
reader.endObject();
return new Message(id, text, user, geo);
}
public List<Double> readDoublesArray(JsonReader reader) throws IOException {
List<Double> doubles = new ArrayList<Double>();
reader.beginArray();
while (reader.hasNext()) {
doubles.add(reader.nextDouble());
}
reader.endArray();
return doubles;
}
public User readUser(JsonReader reader) throws IOException {
String username = null;
int followersCount = -1;
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("name")) {
username = reader.nextString();
} else if (name.equals("followers_count")) {
followersCount = reader.nextInt();
} else {
reader.skipValue();
}
}
reader.endObject();
return new User(username, followersCount);
}
Number Handling
这个类允许将一个数字类型的值解析成String跟String类型解析成数字类型.例如:[“1”,”1”]中的值既可以用nextInt()方法去读取,也可以用nextString()方法来读.这样是为了方式数字型读取的时候的损耗,例如:在JavaScript中,像9007199254740993这么大的数字,使用double这样的类型去读的话就会有精度的损耗.所以使用nextString()来读取的话就会减少这个精度的问题.
Non-Execute Prefix
使用json来交换私密信息的web服务器容易受到Cross-site request forgery这种攻击.在这种攻击中,恶意网站通过Html的