四种json解析和xml解析

一、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;    }    }}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 发现自己有很多文档,所以现在整理一下,以防忘了。。。 作为一种轻量级的数据交换格式,json正在逐步取代xml,成...
    泺莫繁华阅读 1,532评论 0 1
  • JSON JSON和XML都是需要解析的 JSON是一种轻量级的数据格式,一般用于数据交互服务器返回给客户端的数据...
    JonesCxy阅读 1,847评论 2 10
  • 在网络开发中,从服务器获取的二进制数据包括: > html > 图片 > 视频 > 音频 > zip 等 除了以上...
    方圆十里不留母狗阅读 784评论 0 0
  • 1.服务器返回的数据: 对于服务器返回来的data,一般有两种格式:JSON/XMLJSON:是民间的;XML:是...
    SoftKnife阅读 541评论 0 5
  • 在网络开发中,从服务器获取的二进制数据包括: > html >图片 >视频 >音频 > zip等 除了以上文件格式...
    小番茄阳阳阅读 769评论 0 0