一、iOS 4種JSON數據解析方法詳解
2013年09月10日? 綜合? 共 9124字? 字號小中大?評論關閉
在網絡中數據的傳輸很多格式都是JSON或是XML,之前的博文已經介紹過XML,這篇介紹JSON數據。
在對JSON數據進行解析過程中大致有四種方法可供選擇,包括原生的NSJSONSerialization,TouchJson,JSONKit,SBJon;其中后三種方法都要導入第三方類庫。(在使用第三方類庫過程中,如果項目是支持ARC的話,而這些類庫文件不支持ARC特性的話,就會遇到ARC問題保錯,所以就要添加arc特性,即添加-fno-objc-arc就解決)
附:
TouchJson包下載: http://download.csdn.net/detail/enuola/4523169
SBJson 包下載: http://download.csdn.net/detail/enuola/4523177
JSONKit包下載:https://github.com/TouchCode/TouchJSON
一、原生的NSJSONSerialization用法詳解。
You use the NSJSONSerialization class to convert JSON to Foundation objects and convert Foundation objects to JSON.An object that may be converted to JSON must have the following properties://但是轉換成JSON的對象必須具有如下屬性:The top level object is an NSArray or NSDictionary.//頂層對象必須是NSArray或者NSDictionary
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.//所有的對象必須是NSString、NSNumber、NSArray、NSDictionary、NSNull的實例
All dictionary keys are instances of NSString.//所有NSDictionary的key必須是NSString類型
Numbers are not NaN or infinity.//數字對象不能是非數值或無窮
Other rules may apply. CallingisValidJSONObject:or attempting a conversion are the definitive ways to tell if a given object can be converted to JSON data.
這是NSJSONSerialization官方文檔的開頭一段話,介紹的是NSJSONSerialization可以將JSON對象轉換成Foundation對象,也可以將Foundation對象轉換成JSON對象。但是將Foundation對象轉換成JSON對象時就有了上面的要求了。要注意的是The
top level object is an NSArray or NSDictionary。
這個類對應的方法有:
Creating a JSON Object
+ JSONObjectWithData:options:error:
+ JSONObjectWithStream:options:error:
Creating JSON Data
+ dataWithJSONObject:options:error:
+ writeJSONObject:toStream:options:error:
+ isValidJSONObject:
其中紅色標記的方法是常用的方法,下面分別貼出其用法,注意每個方法使用過程中的參數類型。
Returns a Foundation object from given JSON data.
+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error
其中的NSJSONReadingOptions包括:
NSJSONReadingMutableContainers
Specifies that arrays and dictionaries are created as mutable objects.
NSJSONReadingMutableLeaves
Specifies that leaf strings in the JSON object graph are created as instances of NSMutableString.
NSJSONReadingAllowFragments
Specifies that the parser should allow top-level objects that are not an instance of NSArray or NSDictionary.
Returns a Boolean value that indicates whether a given object can be converted to JSON data.
+ (BOOL)isValidJSONObject:(id)obj
Returns JSON data from a Foundation object.
+ (NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error
其中NSJSONWritingOptions包括
NSJSONWritingPrettyPrinted
Specifies that the JSON data should be generated with whitespace designed to make the output more readable. If this option is not set, the most compact possible JSON representation is generated.
下面通過一個簡單的程序例子展示一下這個過程。
NSDictionary *jsonDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:@"value1",@"key1",@"value2",@"key2", nil];
NSError *error;
NSData *jsonData;
if ([NSJSONSerialization isValidJSONObject:jsonDictionary]) {
NSLog(@"yes");
jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:NSJSONWritingPrettyPrinted error:&error];
}
else {
NSLog(@"error :%@",error.localizedDescription);
}
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:&error];
NSLog(@"key1: %@",[dictionary objectForKey:@"key1"]);
二、JSONKit用法詳解
(個人目前用到較多的是使用JSONKit來對json格式的字符串進行解析)
使用JSONKit需要在項目中導入JSONKit類庫文件,可以從上面的鏈接中下載。
使用JSONKit對數據解析過程中常用的方法(都是實例方法)就是:
- (id)objectFromJSONString;
- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
- (id)mutableObjectFromJSONString;
- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
- (id)objectFromJSONData;
- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
- (id)mutableObjectFromJSONData;
- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
常用的分別是前三個。
其中的JKParseOptionFlags包括:
JKSerializeOptionNone ? ? ? ? ? ? ? ? = 0,
JKSerializeOptionPretty ? ? ? ? ? ? ? = (1 << 0),
JKSerializeOptionEscapeUnicode ? ? ? ?= (1 << 1),
JKSerializeOptionEscapeForwardSlashes = (1 << 4),
JKSerializeOptionValidFlags ? ? ? ? ? = (JKSerializeOptionPretty | JKSerializeOptionEscapeUnicode | JKSerializeOptionEscapeForwardSlashes),
下面代碼實例演示解析字符串JSON格式:
NSString *jsonString =@"[{\"precision\": \"zip\",\"Latitude\":? 37.7668,\"Longitude\": -122.3959,\"Address\":? \"\",\"City\":? ? ? \"SAN FRANCISCO\",\"State\":? ? \"CA\",\"Zip\":? ? ? \"94107\",\"Country\":? \"US\"},{\"precision\": \"zip\",\"Latitude\":? 37.371991,\"Longitude\": -122.026020,\"Address\":? \"\",\"City\":? ? ? \"SUNNYVALE\",\"State\":? ? \"CA\",\"Zip\":? ? ? \"94085\",\"Country\":? \"US\"}]";
NSArray *jsonArray = [jsonString objectFromJSONString];
for(NSDictionary *dictionary in jsonArray) {
NSLog(@"City :%@",[dictionary objectForKey:@"City"]);
}
其中的jsonString是如下內容(有兩個dictionary,所以我們可以用一個數組來裝載,然后再用字典來分別裝載里面內容):
[
{
\"precision\": \"zip\",
\"Latitude\": 37.7668,
\"Longitude\": -122.3959,
\"Address\": \"\",
\"City\": \"SANFRANCISCO\",
\"State\": \"CA\",
\"Zip\": \"94107\",
\"Country\": \"US\"
},
{
\"precision\": \"zip\",
\"Latitude\": 37.371991,
\"Longitude\": -122.026020,
\"Address\": \"\",
\"City\": \"SUNNYVALE\",
\"State\": \"CA\",
\"Zip\": \"94085\",
\"Country\": \"US\"
}
]
三、TouchJson用法詳解
首先根據上面給出的下載鏈接下載這個庫,解壓后在項目導入Source文件夾。使用時要如下import相關文件。
#import "Source/JSON/CJSONDeserializer.h"
#import "Source/JSON/CJSONSerializer.h"
提示:如果是ARC項目,在target中關于TouchJson的所有文件請添加arc:-fno-objc-arc。
由上面的import的語句可知,使用這個庫,既可以反序列化操作,即將JSON數據轉換成其他對象,也可以序列化操作,即將某個對象轉換成JSON數據。
1、下面看看序列化操作對應的方法吧!在CJSONSerializer.h文件中可以找到。
+ (CJSONSerializer *)serializer;
- (BOOL)isValidJSONObject:(id)inObject;
/// Take any JSON compatible object (generally NSNull, NSNumber, NSString, NSArray and NSDictionary) and produce an NSData containing the serialized JSON.
- (NSData *)serializeObject:(id)inObject error:(NSError **)outError;
- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError;
- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError;
- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError;
- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError;
- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError;
上面的方法中常用的是前三個,而且注意:Take any JSON compatible object (generally NSNull, NSNumber, NSString, NSArray and NSDictionary) and produce an NSData containing the serialized JSON.下面看看實例代碼吧!
NSError *error;
NSString *jsonString =@"{\"key1\":{\"precision\": \"zip\",\"Latitude\":? 37.7668,\"Longitude\": -122.3959,\"Address\":? \"\",\"City\":? ? ? \"SAN FRANCISCO\",\"State\":? ? \"CA\",\"Zip\":? ? ? \"94107\",\"Country\":? \"US\"},\"key2\":{\"precision\": \"zip\",\"Latitude\":? 37.371991,\"Longitude\": -122.026020,\"Address\":? \"\",\"City\":? ? ? \"SUNNYVALE\",\"State\":? ? \"CA\",\"Zip\":? ? ? \"94085\",\"Country\":? \"US\"}}";
CJSONSerializer *serial = [CJSONSerializer serializer];? //creat an object of CJSONSerializer
if ([serial isValidJSONObject:jsonString]) {
NSLog(@"yes");
NSData *jsonData = [serial serializeString:jsonString error:&error];
}
}
2、接著就是反序列化操作(通常說的數據解析),在CJSONDeserializer.h文件中我們可看到相應的方法。
+ (CJSONDeserializer *)deserializer;
- (id)deserialize:(NSData *)inData error:(NSError **)outError;
- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError;
- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError;
都比較容易理解,常用的還是前兩個。看看實例代碼吧!
NSString *jsonString =@"{\"key1\":{\"precision\": \"zip\",\"Latitude\":? 37.7668,\"Longitude\": -122.3959,\"Address\":? \"\",\"City\":? ? ? \"SAN FRANCISCO\",\"State\":? ? \"CA\",\"Zip\":? ? ? \"94107\",\"Country\":? \"US\"},\"key2\":{\"precision\": \"zip\",\"Latitude\":? 37.371991,\"Longitude\": -122.026020,\"Address\":? \"\",\"City\":? ? ? \"SUNNYVALE\",\"State\":? ? \"CA\",\"Zip\":? ? ? \"94085\",\"Country\":? \"US\"}}";
NSError *error;
NSArray *array = [[CJSONDeserializer deserializer] deserialize:[jsonString dataUsingEncoding:NSUTF8StringEncoding] error:&error];
for(NSDictionary *dic in array) {
NSLog(@"City :%@",[dic objectForKey:@"City"]);
}
上面是將一個json字符串編碼成jsona格式,然后對其進行解析也即反序列化。
四、SBJson用法詳解
在使用SBJson對數據進行解析時,要導入相應的文件,并#import "SBJson/SBJsonParser.h",主要使用的方法有:
- (id)objectWithData:(NSData*)data;
- (id)objectWithString:(NSString *)repr;
- (id)objectWithString:(NSString*)jsonText
error:(NSError**)error;
下面實例演示如何使用SBJson對數據進行解析。
NSString *jsonString =@"{\"key1\":{\"precision\": \"zip\",\"Latitude\":? 37.7668,\"Longitude\": -122.3959,\"Address\":? \"\",\"City\":? ? ? \"SAN FRANCISCO\",\"State\":? ? \"CA\",\"Zip\":? ? ? \"94107\",\"Country\":? \"US\"},\"key2\":{\"precision\": \"zip\",\"Latitude\":? 37.371991,\"Longitude\": -122.026020,\"Address\":? \"\",\"City\":? ? ? \"SUNNYVALE\",\"State\":? ? \"CA\",\"Zip\":? ? ? \"94085\",\"Country\":? \"US\"}}";
NSError *error;
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSDictionary *dictionary= [parser objectWithString:jsonString error:&error];
NSLog(@"%@",dictionary);
NSLog(@"%@",[[dictionary objectForKey:@"key1"] objectForKey:@"City"]);
解釋一下,這里的jsonString和前面使用的有一點區別,待會看輸出就知道了(為兩個dictionary添加了key1和key2)。
首先要創建一個SBJsonParser對象,然后調用方法進行解析。
{
key1 =? ? {
Address = "";
City = "SAN FRANCISCO";
Country = US;
Latitude = "37.7668";
Longitude = "-122.3959";
State = CA;
Zip = 94107;
precision = zip;
};
key2 =? ? {
Address = "";
City = SUNNYVALE;
Country = US;
Latitude = "37.371991";
Longitude = "-122.02602";
State = CA;
Zip = 94085;
precision = zip;
};
}
二、xml解析
? ? 這里首先明白什么是SAX是一個用于處理XML事件驅動的“推”模型,優點是一種解析速度快并且占用內存少的xml解析器,它需要哪些數據再加載和解析哪些內容。缺點是它不會記錄標簽的關系,而要讓你的應用程序自己處理,這樣就增加了你程序的負擔。DOM是一種文檔對象模型,DOM可以以一種獨立于平臺和語言的方式訪問和修改一個文檔的內容和結構。Dom技術使得用戶頁面可以動態地變化,如可以動態地顯示或隱藏一個元素,改變它們的屬性,增加一個元素等,Dom技術使得頁面的交互性大大地增強。缺點是DOM解析XML文件時,會將XML文件的所有內容以文檔樹方式存放在內存中。
Pull解析和Sax解析很相似,Pull解析和Sax解析不一樣的地方是pull讀取xml文件后觸發相應的事件調用方法返回的是數字還有pull可以在程序中控制想解析到哪里就可以停止解析。
以penson.xml文件為例:
name
男
30
第一種SAX 解析代碼如下
public class SaxParseService extends DefaultHandler{
private List mPensons = null;
private Penson mPenson = null;
private String m_preTag = null;
public List getPensons(InputStream xmlStream) throws Exception{?? ??? ? ????? ? ? ? ? SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
SaxParseService handler = new SaxParseService();?? ??? ???????? ? ? ? ? ? ? ? ? ? ? ? parser.parse(xmlStream, handler);
return handler.getPensons();
}
public List getPensons(){
return mPensons;
}
@Override
public void startDocument() throws SAXException {
mPensons = new ArrayList();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if("Penson".equals(qName)){
mPenson = new Penson();?? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? mPenson.penId=Integer.parseInt(attributes.getValue(0));
}
m_preTag = qName;//將正在解析的節點名稱賦給preTag
}
@Override
public void endElement(String uri, String localName, String qName)throws SAXException {
if("Penson".equals(qName)){
mPensons.add(mPenson);
mPenson = null;
}
m_preTag = null;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {?? ??? ? ? ? ?? if(m_preTag!=null){
try {
String content = new String(ch,start,length);?? ??? ??? ?????????????????? if("name".equals(m_preTag)){
mPenson.name=content;
}else if("sex".equals(m_preTag)){
mPenson.sex=content;
}else if("age".equals(m_preTag)){?? ??? ??? ??? ????????????????????? mPenson.age=Integer.parseInt(content);
} }catch(Exception e){}?? ??? ?}?? ?}}
第二種PULL 解析代碼如下:
public class PullParseService {
public static List getPensons(InputStream inputStream) throws Exception{ ??? ????? List mPensons = null;
Penson mPenson = null;
XmlPullParser parser = Xml.newPullParser(); ??? ???????????????????????????????? parser.setInput(inputStream, "UTF-8");
int event = parser.getEventType();?? ???????? while(event!=XmlPullParser.END_DOCUMENT){
switch(event){
case XmlPullParser.START_DOCUMENT:
mPensons = new ArrayList();
break;
case XmlPullParser.START_TAG:?? ???????????????? if("Penson".equals(parser.getName())){
mPenson = new Penson(); ??? ???????????????????? mPenson.penId=Integer.parseInt(parser.getAttributeValue(0));?? ???????????????? } ??? ???????????????? if(mPenson!=null){ ??? ???????????????????? if("name".equals(parser.getName())){?? ???????????????????????? mPenson.name=parser.nextText();
}else if("sex".equals(parser.getName())){?? ???????????????????????? mPenson.sex=parser.nextText();
}else if("age".equals(parser.getName())){?? ???????????????????????? mPenson.age=Integer.parseInt(parser.nextText()); ??? ???????????????????? }? ??? ? } ? break;
case XmlPullParser.END_TAG:?? ???????????????? if("Penson".equals(parser.getName())){
mPensons.add(mPenson);
mPenson = null; ??? ???????????????? } ??? ???????????????? break; ??? ???????????? }
event = parser.next();
}
return mPensons; ??? ???? } ?}
第三種DOM 解析代碼如下:
public class DomParseService {
public List getPensons(InputStream inputStream) throws Exception{?? ??? ?????? List list = new ArrayList();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();?? ??? ? ? ? ?? DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(inputStream);
Element element = document.getDocumentElement();
NodeList PensonNodes = element.getElementsByTagName("Penson");
for(int i=0;i
Element PensonElement = (Element) PensonNodes.item(i);
Penson mPenson = new Penson();?? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? mPenson.penId=Integer.parseInt(PensonElement.getAttribute("id"));?? ??? ??? ?????????? NodeList childNodes = PensonElement.getChildNodes();
for(int j=0;j
if("name".equals(childNodes.item(j).getNodeName())){
mPenson.name=childNodes.item(j).getFirstChild().getNodeValue();?? ??? ??? ??? ??? ?}else if("sex".equals(childNodes.item(j).getNodeName())){?? ??? ??? ??? ??? ??? ?mPenson.sex=childNodes.item(j).getFirstChild().getNodeValue();?? ??? ??? ??? ??? ?}else if("age".equals(childNodes.item(j).getNodeName())){?? ??? ??? ??? ??? ??? ?mPenson.age=Integer.parseInt(childNodes.item(j).getFirstChild().getNodeValue());?? ??? ??? ??? ??? ?}?? ??? ??? ??? ?}?? ??? ??? ?}
list.add(mPenson);
}
return list;?? ?}}
測試代碼
public class MainActivity extends Activity implements OnClickListener{
private TextView mText;
@Override
protected void onCreate(Bundle savedInstanceState) {?? ??? ?super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText=(TextView) findViewById(R.id.text);?? ??? ?findViewById(R.id.btn_sax).setOnClickListener(this);?? ??? ?findViewById(R.id.btn_pull).setOnClickListener(this);?? ??? ?findViewById(R.id.btn_dom).setOnClickListener(this);
testSaxXml();
}
public void testSaxXml()?? ?{
try {
InputStream input=this.getResources().getAssets().open("peson.xml");?? ??? ????????? SaxParseService sax = new SaxParseService();
List mPensons=sax.getPensons(input);
for(Penson mPenson : mPensons){
String text= "sax analyze:"+mPenson.toString();??? ??? ??? ???????????????????? mText.setText(text);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} ??? ?}
public void testPullXml()?? ?{
try {
InputStream input=this.getResources().getAssets().open("peson.xml");?? ??? ? ? ? ? ? ? PullParseService pull = new PullParseService();
List mPensons=pull.getPensons(input);
for(Penson mPenson : mPensons)? {
String text= "pull analyze:"+mPenson.toString();
mText.setText(text);
}?? ??? ?} catch (Exception e) {?? ??? ??? ?// TODO Auto-generated catch block?? ??? ??? ?e.printStackTrace();?? ??? ?}?? ?}
public void testDomXml()?? ?{?? ??? ?try {
InputStream input=this.getResources().getAssets().open("peson.xml");?? ??? ?DomParseService dom = new DomParseService();
List mPensons=dom.getPensons(input);
for(Penson mPenson : mPensons)?? ??? ?{
String text= "dom analyze:"+mPenson.toString();??? ??? ??? ??? ?? mText.setText(text);
}?? ??? ?} catch (Exception e) {?? ??? ??? ?// TODO Auto-generated catch block?? ??? ??? ?e.printStackTrace();?? ??? ?}?? ?}
@Override
public void onClick(View v)??
??{
switch (v.getId())???
?{
case R.id.btn_sax:
testSaxXml();
break;
case R.id.btn_pull:
testPullXml();
break;
case R.id.btn_dom:
testDomXml();
break;?? ?}?? ?}}