XML解析和Xml转Json
2017-12-28 本文已影响588人
奔跑吧李博
Xml的使用在Android开发中也时不时地遇到,通常xml用来写配置文件。Xml解析也是Android开发人员必备的一项技术点。常见的XML解析为DOM解析,SAX解析和PULL解析。
DOM解析:
一次性将全部内容加载到内存中,生成树状结构,可随机访问XML文档中的各个部分,因此检索和更新效率高。
SAX解析:
使用流式处理的方式,它并不记录所读内容的相关信息。它是一种以事件为驱动的XML API,解析速度快,占用内存少。使用回调函数来实现。缺点是不能倒退。
PULL解析:
PULL与SAX类似,都提供了类似的事件,都是从开始到结束扫描元素。不同的是,PULL解析触发的事件不是一个方法,而是一个数字。使用方便,效率高。
三种解析比较:
- 内存占用:SAX、PULL比DOM要好。
- 编程方式:SAX 采用事件驱动,在相应事件触发的时候,会调用用户编好的方法,也即每解析一类 XML,就要编写一个新的适合该类XML的处理类。DOM 是 W3C 的规范,Pull 简洁。
- 访问方式:SAX采用流式解析,DOM随机访问。
PULL解析操作实例:
1. 写一份需要解析的xml文件,在res下新建raw资源文件夹,放入其中,内容是关于军事新闻的:
<?xml version="1.0" encoding="utf-8"?>
<news>
<item name="国产航母下水" textnum="35">
<content>
新型武器方面,中国的主要成就有远程导弹,国产航母,新型驱逐舰,两栖舰,隐身战斗机,无人机,网络战能力。
</content>
<imageurl>
http://n.sinaimg.cn/sinacn/w640h400/20171220/6a0a-fypvuqe3950199.jpg
</imageurl>
</item>
<item name="055型导弹驱逐舰下水" textnum="94">
<content>
中国的造舰速度已经远超美国,按照五角大楼的说法,在未来15年内,中国将继续服役5艘航母。届时中国将拥有6艘航母和对应的先进舰载机、武器系统。此外,中国还将继续生产071型两栖登陆舰、055型大型驱逐舰、弹道导弹战略核潜艇等。这些性能先进的武器将彻底影响美国海军在亚太地区乃至全球的战略考量。
</content>
<imageurl>
http://n.sinaimg.cn/sinacn/w640h371/20171220/987f-fypvuqe3953002.jpg
</imageurl>
</item>
<item name="歼-20战斗机" textnum="118">
<content>
中国空军发展同样迅速:隐身战斗机、远程空空导弹、大型预警机、战略运输机都将被重点部署。解放军目前拥有大约2200架战机,其中600架被认为是现代化的,包括了歼-10、歼-11、苏-27/30、苏-35和歼-20。如今的中国空军,正在从一支固守本土、能力有限的部队提升为一支现代化、全球化的大型空天军,美军将遭遇冷战以来的最强劲对手。
</content>
<imageurl>
http://n.sinaimg.cn/sinacn/w640h480/20171220/fe4f-fypvuqe3953079.jpg
</imageurl>
</item>
</news>
2. 根据xml内容创建实体类:
public class NewsBean {
private String name;
private int length;
private String content;
private String imageUrl;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}
3. 使用PULL解析方式进行解析
Pull解析步骤:
1.创建XmlPullParser对象parser,获取资源文件流,将流传入XmlPullParser对象,并且创建对象集合
2.parser.getEventType()获取当前解析的标签,循环进行判断
3.当标签问核心对象起始标签,创建对象;判断各种标签名,parser.getAttributeValue()获取标签内部值,parser.nextText()获取标签之间的值。
4.当判断为核心对象结束标签,将对象添加到集合中并给对象赋空,循环结束或文档结束返回集合。
import android.content.Context;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class PullParser {
public static List<NewsBean> pullParse(Context context){
ArrayList<NewsBean> list = new ArrayList<>();
NewsBean NewsBean = null;
try {
//获取资源文件流
InputStream inputStream = context.getResources().openRawResource(R.raw.news);
XmlPullParser parser = Xml.newPullParser();
//设置文件流和编码方式
parser.setInput(inputStream,"UTF-8");
//得到解析的事件类型
int eventType = parser.getEventType();
//循环读取标签知道文档读取结束
while (eventType != XmlPullParser.END_DOCUMENT){
switch (eventType){
case XmlPullParser.START_TAG:
//item起始标签,创建对象
String tag = parser.getName();
if(tag.equalsIgnoreCase("item")){
NewsBean = new NewsBean();
NewsBean.setName(parser.getAttributeValue(null,"name"));
NewsBean.setLength(Integer.parseInt(parser.getAttributeValue(null,"textnum")));
}else if(tag.equalsIgnoreCase("content")){
NewsBean.setContent(parser.nextText());
}else if(tag.equalsIgnoreCase("imageurl")){
NewsBean.setImageUrl(parser.nextText());
}
break;
case XmlPullParser.END_TAG:
//遇到对象结束标签,将对象添加到集合中
if(parser.getName().equalsIgnoreCase("item") && NewsBean != null){
list.add(NewsBean);
NewsBean = null;
}
break;
}
eventType = parser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
}
4. MainActivity完整代码:xmlParse()方式和xmlToJson()方式任选其一
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.ImageView;
import com.alibaba.fastjson.JSON;
import com.bumptech.glide.Glide;
import com.zhy.adapter.recyclerview.CommonAdapter;
import com.zhy.adapter.recyclerview.base.ViewHolder;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private List<NewsBean> datas = new ArrayList<>();
private CommonAdapter<NewsBean> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setAdapter();
//xmlParse();
xmlToJson();
}
private void setAdapter(){
recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
adapter = new CommonAdapter<NewsBean>(getApplicationContext(),R.layout.item,datas) {
@Override
protected void convert(ViewHolder holder, NewsBean NewsBean, int position) {
ImageView ivCover = holder.getView(R.id.iv_cover);
Glide.with(getApplicationContext()).load(NewsBean.getImageUrl().trim()).into(ivCover);
holder.setText(R.id.tv_title,NewsBean.getName());
holder.setText(R.id.tv_content,NewsBean.getContent());
holder.setText(R.id.tv_num,"字数 " + NewsBean.getLength());
}
};
recyclerView.setAdapter(adapter);
}
/**
* pull解析方式
*/
private void xmlParse(){
List list = PullParser.pullParse(getApplicationContext());
datas.addAll(list);
adapter.notifyDataSetChanged();
}
/**
* xml转json方式
* @return
*/
private int xmlToJson() {
try {
InputStream inputStream = getResources().openRawResource(R.raw.news);
XmlToJson xmlToJson = new XmlToJson.Builder(inputStream, null).build();
JSONArray array = xmlToJson.toJson().getJSONObject("news").getJSONArray("item");
List list = JSON.parseArray(array.toString(),NewsBean.class);
datas.addAll(list);
adapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
return 0;
}
}
XmlToJson转成json结果:
{
"news": {
"item": [
{
"content": "\n 新型武器方面,中国的主要成就有远程导弹,国产航母,新型驱逐舰,两栖舰,隐身战斗机,无人机,网络战能力。\n ",
"name": "国产航母下水",
"textnum": 35,
"imageurl": "\n http://n.sinaimg.cn/sinacn/w640h400/20171220/6a0a-fypvuqe3950199.jpg\n "
},
{
"content": "\n 中国的造舰速度已经远超美国,按照五角大楼的说法,在未来15年内,中国将继续服役5艘航母。届时中国将拥有6艘航母和对应的先进舰载机、武器系统。此外,中国还将继续生产071型两栖登陆舰、055型大型驱逐舰、弹道导弹战略核潜艇等。这些性能先进的武器将彻底影响美国海军在亚太地区乃至全球的战略考量。\n ",
"name": "055型导弹驱逐舰下水",
"textnum": 94,
"imageurl": "\n http://n.sinaimg.cn/sinacn/w640h371/20171220/987f-fypvuqe3953002.jpg\n "
},
{
"content": "\n 中国空军发展同样迅速:隐身战斗机、远程空空导弹、大型预警机、战略运输机都将被重点部署。解放军目前拥有大约2200架战机,其中600架被认为是现代化的,包括了歼-10、歼-11、苏-27/30、苏-35和歼-20。如今的中国空军,正在从一支固守本土、能力有限的部队提升为一支现代化、全球化的大型空天军,美军将遭遇冷战以来的最强劲对手。\n ",
"name": "歼-20战斗机",
"textnum": 118,
"imageurl": "\n http://n.sinaimg.cn/sinacn/w640h480/20171220/fe4f-fypvuqe3953079.jpg\n "
}
]
}
}
效果图:

这里是github代码直通车
附:最后附上XmlToJson完整代码:
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
/**
* Converts XML to JSON
*/
public class XmlToJson {
private static final String TAG = "XmlToJson";
private static final String DEFAULT_CONTENT_NAME = "content";
private static final String DEFAULT_ENCODING = "utf-8";
private static final String DEFAULT_INDENTATION = " ";
private String mIndentationPattern = DEFAULT_INDENTATION;
/**
* Builder class to create a XmlToJson object
*/
public static class Builder {
private StringReader mStringSource;
private InputStream mInputStreamSource;
private String mInputEncoding = DEFAULT_ENCODING;
private HashSet<String> mForceListPaths = new HashSet<>();
private HashMap<String, String> mAttributeNameReplacements = new HashMap<>();
private HashMap<String, String> mContentNameReplacements = new HashMap<>();
private HashSet<String> mForceStringForPath = new HashSet<>();
private HashSet<String> mSkippedAttributes = new HashSet<>();
private HashSet<String> mSkippedTags = new HashSet<>();
/**
* Constructor
*
* @param xmlSource XML source
*/
public Builder(@NonNull String xmlSource) {
mStringSource = new StringReader(xmlSource);
}
/**
* Constructor
*
* @param inputStreamSource XML source
* @param inputEncoding XML encoding format, can be null (uses UTF-8 if null).
*/
public Builder(@NonNull InputStream inputStreamSource, @Nullable String inputEncoding) {
mInputStreamSource = inputStreamSource;
mInputEncoding = (inputEncoding != null) ? inputEncoding : DEFAULT_ENCODING;
}
/**
* Force a XML Tag to be interpreted as a list
*
* @param path Path for the tag, with format like "/parentTag/childTag/tagAsAList"
* @return the Builder
*/
public Builder forceList(@NonNull String path) {
mForceListPaths.add(path);
return this;
}
/**
* Change the name of an attribute
*
* @param attributePath Path for the attribute, using format like "/parentTag/childTag/childTagAttribute"
* @param replacementName Name used for replacement (childTagAttribute becomes replacementName)
* @return the Builder
*/
public Builder setAttributeName(@NonNull String attributePath, @NonNull String replacementName) {
mAttributeNameReplacements.put(attributePath, replacementName);
return this;
}
/**
* Change the name of the key for a XML content
* In XML there is no extra key name for a tag content. So a default name "content" is used.
* This "content" name can be replaced with a custom name.
*
* @param contentPath Path for the Tag that holds the content, using format like "/parentTag/childTag"
* @param replacementName Name used in place of the default "content" key
* @return the Builder
*/
public Builder setContentName(@NonNull String contentPath, @NonNull String replacementName) {
mContentNameReplacements.put(contentPath, replacementName);
return this;
}
/**
* Force an attribute on content value to be a String (by default the library converts numbers to Integer or Double)
*
* @param path Path for the Tag content or Attribute, using format like "/parentTag/childTag"
* @return the Builder
*/
public Builder forceStringForPath(@NonNull String path) {
mForceStringForPath.add(path);
return this;
}
/**
* Skips a Tag (will not be present in the JSON)
*
* @param path Path for the Tag, using format like "/parentTag/childTag"
* @return the Builder
*/
public Builder skipTag(@NonNull String path) {
mSkippedTags.add(path);
return this;
}
/**
* Skips an attribute (will not be present in the JSON)
*
* @param path Path for the Attribute, using format like "/parentTag/childTag/ChildTagAttribute"
* @return the Builder
*/
public Builder skipAttribute(@NonNull String path) {
mSkippedAttributes.add(path);
return this;
}
/**
* Creates the XmlToJson object
*
* @return a XmlToJson instance
*/
public XmlToJson build() {
return new XmlToJson(this);
}
}
private StringReader mStringSource;
private InputStream mInputStreamSource;
private String mInputEncoding;
private HashSet<String> mForceListPaths;
private HashMap<String, String> mAttributeNameReplacements;
private HashMap<String, String> mContentNameReplacements;
private HashSet<String> mForceStringForPath;
private HashSet<String> mSkippedAttributes = new HashSet<>();
private HashSet<String> mSkippedTags = new HashSet<>();
private JSONObject mJsonObject; // Used for caching the result
private XmlToJson(Builder builder) {
mStringSource = builder.mStringSource;
mInputStreamSource = builder.mInputStreamSource;
mInputEncoding = builder.mInputEncoding;
mForceListPaths = builder.mForceListPaths;
mAttributeNameReplacements = builder.mAttributeNameReplacements;
mContentNameReplacements = builder.mContentNameReplacements;
mForceStringForPath = builder.mForceStringForPath;
mSkippedAttributes = builder.mSkippedAttributes;
mSkippedTags = builder.mSkippedTags;
mJsonObject = convertToJSONObject(); // Build now so that the InputStream can be closed just after
}
/**
* @return the JSONObject built from the XML
*/
public
@Nullable
JSONObject toJson() {
return mJsonObject;
}
private
@Nullable
JSONObject convertToJSONObject() {
try {
Tag parentTag = new Tag("", "xml");
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false); // tags with namespace are taken as-is ("namespace:tagname")
XmlPullParser xpp = factory.newPullParser();
setInput(xpp);
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.START_DOCUMENT) {
eventType = xpp.next();
}
readTags(parentTag, xpp);
unsetInput();
return convertTagToJson(parentTag, false);
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
return null;
}
}
private void setInput(XmlPullParser xpp) {
if (mStringSource != null) {
try {
xpp.setInput(mStringSource);
} catch (XmlPullParserException e) {
e.printStackTrace();
}
} else {
try {
xpp.setInput(mInputStreamSource, mInputEncoding);
} catch (XmlPullParserException e) {
e.printStackTrace();
}
}
}
private void unsetInput() {
if (mStringSource != null) {
mStringSource.close();
}
// else the InputStream has been given by the user, it is not our role to close it
}
private void readTags(Tag parent, XmlPullParser xpp) {
try {
int eventType;
do {
eventType = xpp.next();
if (eventType == XmlPullParser.START_TAG) {
String tagName = xpp.getName();
String path = parent.getPath() + "/" + tagName;
boolean skipTag = mSkippedTags.contains(path);
Tag child = new Tag(path, tagName);
if (!skipTag) {
parent.addChild(child);
}
// Attributes are taken into account as key/values in the child
int attrCount = xpp.getAttributeCount();
for (int i = 0; i < attrCount; ++i) {
String attrName = xpp.getAttributeName(i);
String attrValue = xpp.getAttributeValue(i);
String attrPath = parent.getPath() + "/" + child.getName() + "/" + attrName;
// Skip Attributes
if (mSkippedAttributes.contains(attrPath)) {
continue;
}
attrName = getAttributeNameReplacement(attrPath, attrName);
Tag attribute = new Tag(attrPath, attrName);
attribute.setContent(attrValue);
child.addChild(attribute);
}
readTags(child, xpp);
} else if (eventType == XmlPullParser.TEXT) {
String text = xpp.getText();
parent.setContent(text);
} else if (eventType == XmlPullParser.END_TAG) {
return;
} else {
Log.i(TAG, "unknown xml eventType " + eventType);
}
} while (eventType != XmlPullParser.END_DOCUMENT);
} catch (XmlPullParserException | IOException | NullPointerException e) {
e.printStackTrace();
}
}
private JSONObject convertTagToJson(Tag tag, boolean isListElement) {
JSONObject json = new JSONObject();
// Content is injected as a key/value
if (tag.getContent() != null) {
String path = tag.getPath();
String name = getContentNameReplacement(path, DEFAULT_CONTENT_NAME);
putContent(path, json, name, tag.getContent());
}
try {
HashMap<String, ArrayList<Tag>> groups = tag.getGroupedElements(); // groups by tag names so that we can detect lists or single elements
for (ArrayList<Tag> group : groups.values()) {
if (group.size() == 1) { // element, or list of 1
Tag child = group.get(0);
if (isForcedList(child)) { // list of 1
JSONArray list = new JSONArray();
list.put(convertTagToJson(child, true));
String childrenNames = child.getName();
json.put(childrenNames, list);
} else { // stand alone element
if (child.hasChildren()) {
JSONObject jsonChild = convertTagToJson(child, false);
json.put(child.getName(), jsonChild);
} else {
String path = child.getPath();
putContent(path, json, child.getName(), child.getContent());
}
}
} else { // list
JSONArray list = new JSONArray();
for (Tag child : group) {
list.put(convertTagToJson(child, true));
}
String childrenNames = group.get(0).getName();
json.put(childrenNames, list);
}
}
return json;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
private void putContent(String path, JSONObject json, String tag, String content) {
try {
if (content != null) {
if (mForceStringForPath.contains(path)) {
json.put(tag, content);
} else if (content.equalsIgnoreCase("true")) {
json.put(tag, true);
} else if (content.equalsIgnoreCase("false")) {
json.put(tag, false);
} else {
try {
Integer integer = Integer.parseInt(content);
json.put(tag, integer);
} catch (NumberFormatException exceptionInt) {
try {
Double number = Double.parseDouble(content);
json.put(tag, number.doubleValue());
} catch (NumberFormatException exceptionDouble) {
json.put(tag, content);
}
}
}
}
} catch (JSONException exception) {
}
}
private boolean isForcedList(Tag tag) {
String path = tag.getPath();
return mForceListPaths.contains(path);
}
private String getAttributeNameReplacement(String path, String defaultValue) {
String result = mAttributeNameReplacements.get(path);
if (result != null) {
return result;
}
return defaultValue;
}
private String getContentNameReplacement(String path, String defaultValue) {
String result = mContentNameReplacements.get(path);
if (result != null) {
return result;
}
return defaultValue;
}
@Override
public String toString() {
if (mJsonObject != null) {
return mJsonObject.toString();
}
return null;
}
/**
* Format the Json with indentation and line breaks
*
* @param indentationPattern indentation to use, for example " " or "\t".
* if null, use the default 3 spaces indentation
* @return the formatted Json
*/
public String toFormattedString(@Nullable String indentationPattern) {
if (indentationPattern == null) {
mIndentationPattern = DEFAULT_INDENTATION;
} else {
mIndentationPattern = indentationPattern;
}
return toFormattedString();
}
/**
* Format the Json with indentation and line breaks.
* Uses the last intendation pattern used, or the default one (3 spaces)
*
* @return the Builder
*/
public String toFormattedString() {
if (mJsonObject != null) {
String indent = "";
StringBuilder builder = new StringBuilder();
builder.append("{\n");
format(mJsonObject, builder, indent);
builder.append("}\n");
return builder.toString();
}
return null;
}
private void format(JSONObject jsonObject, StringBuilder builder, String indent) {
Iterator<String> keys = jsonObject.keys();
while (keys.hasNext()) {
String key = keys.next();
builder.append(indent);
builder.append(mIndentationPattern);
builder.append("\"");
builder.append(key);
builder.append("\": ");
Object value = jsonObject.opt(key);
if (value instanceof JSONObject) {
JSONObject child = (JSONObject) value;
builder.append(indent);
builder.append("{\n");
format(child, builder, indent + mIndentationPattern);
builder.append(indent);
builder.append(mIndentationPattern);
builder.append("}");
} else if (value instanceof JSONArray) {
JSONArray array = (JSONArray) value;
formatArray(array, builder, indent + mIndentationPattern);
} else {
formatValue(value, builder);
}
if (keys.hasNext()) {
builder.append(",\n");
} else {
builder.append("\n");
}
}
}
private void formatArray(JSONArray array, StringBuilder builder, String indent) {
builder.append("[\n");
for (int i = 0; i < array.length(); ++i) {
Object element = array.opt(i);
if (element instanceof JSONObject) {
JSONObject child = (JSONObject) element;
builder.append(indent);
builder.append(mIndentationPattern);
builder.append("{\n");
format(child, builder, indent + mIndentationPattern);
builder.append(indent);
builder.append(mIndentationPattern);
builder.append("}");
} else if (element instanceof JSONArray) {
JSONArray child = (JSONArray) element;
formatArray(child, builder, indent + mIndentationPattern);
} else {
formatValue(element, builder);
}
if (i < array.length() - 1) {
builder.append(",");
}
builder.append("\n");
}
builder.append(indent);
builder.append("]");
}
private void formatValue(Object value, StringBuilder builder) {
if (value instanceof String) {
String string = (String) value;
builder.append("\"");
builder.append(string);
builder.append("\"");
} else if (value instanceof Long) {
Long longValue = (Long) value;
builder.append(longValue);
} else if (value instanceof Integer) {
Integer intValue = (Integer) value;
builder.append(intValue);
} else if (value instanceof Boolean) {
Boolean bool = (Boolean) value;
builder.append(bool);
} else if (value instanceof Double) {
Double db = (Double) value;
builder.append(db);
} else {
builder.append(value.toString());
}
}
}