Android編程實(shí)現(xiàn)XML解析與保存的三種方法詳解
本文實(shí)例講述了Android編程實(shí)現(xiàn)XML解析與保存的三種方法。分享給大家供大家參考,具體如下:
簡(jiǎn)介
在Android開(kāi)發(fā)中,關(guān)于XML解析有三種方式,分別是:
1. SAX 基于事件的解析器,解析速度快,占用內(nèi)存少。非常適合在Android移動(dòng)設(shè)備中使用。
2. DOM 在內(nèi)存中以樹(shù)形結(jié)構(gòu)存放,因此檢索和更新效率會(huì)更高。但是對(duì)于特別大的文檔,解析和加載整個(gè)文檔將會(huì)很耗資源
3. PULL 基于事件的解析器,不同于SAX是,PULL是主動(dòng)請(qǐng)求下一個(gè)事件,所以在可控上PULL要比SAX實(shí)用。Android系統(tǒng)內(nèi)部在解析各種XML時(shí)也是用PULL解析器。
下面實(shí)現(xiàn)各種解析的類(lèi)以及工程結(jié)構(gòu)

這是我們將要解析的XML文件id.xml以及其對(duì)應(yīng)的Book類(lèi)
<?xml version="1.0" encoding="UTF-8"?> <books> <book id="1001"> <name>Thinking In Java</name> <price>80.0</price> </book> <book id="1002"> <name>Core Java</name> <price>90.0</price> </book> <book id="1003"> <name>Hello, Andriod</name> <price>100.0</price> </book> </books>
Book.Java
public class Book {
public static final String BOOKS = "books";
public static final String BOOK = "book";
public static final String ID = "id";
public static final String NAME = "name";
public static final String PRICE = "price";
private int id;
private String name;
private float price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public String toString() {
return "id:" + id + ",name:" + name + ",price:" + price;
}
}
下面是XML解析類(lèi)的實(shí)現(xiàn)
抽象類(lèi)用于 XML文件讀寫(xiě)的繼承
XMLParseFactory.java
import java.io.InputStream;
import java.util.List;
public abstract class XMLParseFactory {
/**
* 讀取指定的XML文件
* @param inputStream XML文件輸入流
*/
public abstract void readXML(InputStream inputStream);
/**
* 保存XML到指定的文件
* @param filePath 文件的絕對(duì)路徑
*/
public abstract void writeXML(String filePath);
/**
* 獲取Book對(duì)象列表
* @return
*/
public abstract List<Book> getBookList();
/**
* 設(shè)置Book對(duì)象列表
*/
public abstract void setBookList(List<Book> bookList);
}
用SAX實(shí)現(xiàn)的方法
SAXParserTool.java
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
public class SAXParserTool extends XMLParseFactory {
// private static final String TAG = "SAXParserTool";
private SAXHandler mHandler = new SAXHandler();
private List<Book> mBookList;
@Override
public void readXML(InputStream inputStream) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(inputStream, mHandler);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void writeXML(String filePath) {
SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
try {
TransformerHandler handler = factory.newTransformerHandler();
Transformer transformer = handler.getTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
FileOutputStream fos = new FileOutputStream(filePath);
OutputStreamWriter writer = new OutputStreamWriter(fos);
Result result = new StreamResult(writer);
handler.setResult(result);
String uri = "";
String localName = "";
handler.startDocument();
handler.startElement(uri, localName, Book.BOOKS, null);
AttributesImpl attrs = new AttributesImpl();
char[] ch = null;
for (Book book:mBookList) {
attrs.clear();
attrs.addAttribute(uri, localName, Book.ID, "string", String.valueOf(book.getId()));
handler.startElement(uri, localName, Book.BOOK, attrs);
handler.startElement(uri, localName, Book.NAME, null);
ch = book.getName().toCharArray();
handler.characters(ch, 0, ch.length);
handler.endElement(uri, localName, Book.NAME);
handler.startElement(uri, localName, Book.PRICE, null);
ch = String.valueOf(book.getPrice()).toCharArray();
handler.characters(ch, 0, ch.length);
handler.endElement(uri, localName, Book.PRICE);
handler.endElement(uri, localName, Book.BOOK);
}
handler.endElement(uri, localName, Book.BOOKS);
handler.endDocument();
} catch (TransformerConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 獲取BookList列表
* @return
*/
public List<Book> getBookList() {
if (mHandler == null) {
return null;
}
return mHandler.getBookList();
}
/**
* 設(shè)置BookList列表
* @param bookList
*/
public void setBookList(List<Book> bookList) {
mBookList = bookList;
}
class SAXHandler extends DefaultHandler {
private List<Book> mBookList;
private Book mBook;
private String mTargetName;
public List<Book> getBookList() {
return mBookList;
}
@Override
public void startDocument() throws SAXException {
super.startDocument();
mBookList = new ArrayList<Book>();
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
if (localName.equals(Book.BOOK)) {
mBook = new Book();
mBook.setId(Integer.valueOf(attributes.getValue(Book.ID)));
}
mTargetName = localName;
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(uri, localName, qName);
if (Book.BOOK.equals(localName)) {
mBookList.add(mBook);
}
mTargetName = null;
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
super.characters(ch, start, length);
if (Book.NAME.equals(mTargetName)) {
mBook.setName(new String(ch, start, length));
} else if (Book.PRICE.equals(mTargetName)) {
mBook.setPrice(Float.valueOf(new String(ch, start, length)));
}
}
}
}
用PULL實(shí)現(xiàn)的方法
PULLParserTool.java
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.util.Xml;
public class PULLParserTool extends XMLParseFactory {
// private static final String TAG = "PULLParserTool";
private List<Book> mBookList;
private Book mBook;
@Override
public void readXML(InputStream inputStream) {
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(inputStream, "UTF-8");
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
String name = parser.getName();
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
mBookList = new ArrayList<Book>();
break;
case XmlPullParser.START_TAG:
// Log.d(TAG, "getName:"+parser.getName()+","+parser.getText());
if (Book.BOOK.equals(name)) {
mBook = new Book();
mBook.setId(Integer.valueOf(parser.getAttributeValue("", Book.ID)));
} else if (Book.NAME.equals(name)) {
mBook.setName(parser.nextText());
} else if (Book.PRICE.equals(name)) {
mBook.setPrice(Float.valueOf(parser.nextText()));
}
break;
case XmlPullParser.END_TAG:
if (Book.BOOK.equals(name)) {
mBookList.add(mBook);
}
break;
default:
break;
}
eventType = parser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void writeXML(String filePath) {
XmlSerializer serializer = Xml.newSerializer();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(filePath);
serializer.setOutput(fos, "UTF-8");
serializer.startDocument("UTF-8", true);
serializer.startTag("", Book.BOOKS);
for (Book book:mBookList) {
serializer.startTag("", Book.BOOK);
serializer.attribute("", Book.ID, book.getId()+"");
serializer.startTag("", Book.NAME);
serializer.text(book.getName());
serializer.endTag("", Book.NAME);
serializer.startTag("", Book.PRICE);
serializer.text(String.valueOf(book.getPrice()));
serializer.endTag("", Book.PRICE);
serializer.endTag("", Book.BOOK);
}
serializer.endTag("", Book.BOOKS);
serializer.endDocument();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public List<Book> getBookList() {
return mBookList;
}
public void setBookList(List<Book> bookList) {
mBookList = bookList;
}
}
用DOM實(shí)現(xiàn)的方法
DOMParserTool.java
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import android.util.Log;
public class DOMParserTool extends XMLParseFactory {
private static final String TAG = "DOMParserTool";
private List<Book> mBookList;
@Override
public void readXML(InputStream inputStream) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document parse = builder.parse(inputStream);
Element root = parse.getDocumentElement();
NodeList nodeList = root.getElementsByTagName(Book.BOOK);
mBookList = new ArrayList<Book>();
Log.d(TAG,"length:"+nodeList.getLength());
for (int i = 0; i < nodeList.getLength(); i++) {
Book book = new Book();
Element item = (Element) nodeList.item(i);
book.setId(Integer.valueOf(item.getAttribute(Book.ID)));
NodeList nodes = item.getChildNodes();
for (int j = 0; j < nodes.getLength(); j++) {
Node node = nodes.item(j);
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (Book.NAME.equals(node.getNodeName())) {
String content = node.getTextContent();
book.setName(content);
} else if (Book.PRICE.equals(node.getNodeName())) {
String content = node.getTextContent();
book.setPrice(Float.valueOf(content));
}
}
Log.d(TAG, "readXML:"+node.getLocalName()+","+node.getNodeName()
+","+node.getNodeType()+","+node.getNodeValue()
+","+node.getTextContent()
+","+node.toString());
}
mBookList.add(book);
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void writeXML(String filePath) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
Element rootElement = document.createElement("books");
for (Book book:mBookList) {
Element bookElement = document.createElement("book");
bookElement.setAttribute(Book.ID, book.getId()+"");
Element nameElement = document.createElement(Book.NAME);
nameElement.setTextContent(book.getName());
bookElement.appendChild(nameElement);
Element priceElement = document.createElement(Book.PRICE);
priceElement.setTextContent(String.valueOf(book.getPrice()));
bookElement.appendChild(priceElement);
rootElement.appendChild(bookElement);
}
document.appendChild(rootElement);
TransformerFactory transfactory = TransformerFactory.newInstance();
Transformer transformer = transfactory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");// 設(shè)置輸出采用的編碼方式
transformer.setOutputProperty(OutputKeys.INDENT, "yes");// 是否自動(dòng)添加額外的空白
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");// 是否忽略XML聲明
FileOutputStream fos = new FileOutputStream(filePath);
Source source = new DOMSource(document);
Result result = new StreamResult(fos);
transformer.transform(source, result);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}
public List<Book> getBookList() {
return mBookList;
}
public void setBookList(List<Book> bookList) {
mBookList = bookList;
}
}
這是調(diào)用各種解析類(lèi)的實(shí)現(xiàn)
MainActivity.java
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import com.example.xmlparsedemo.xmlparser.Book;
import com.example.xmlparsedemo.xmlparser.DOMParserTool;
import com.example.xmlparsedemo.xmlparser.PULLParserTool;
import com.example.xmlparsedemo.xmlparser.SAXParserTool;
import com.example.xmlparsedemo.xmlparser.XMLParseFactory;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {
private TextView mText;
private XMLParseFactory parseFactory;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
Button saxReadBtn = (Button) findViewById(R.id.id_saxread);
Button saxWriteBtn = (Button) findViewById(R.id.id_saxwrite);
Button domReadBtn = (Button) findViewById(R.id.id_domread);
Button domWriteBtn = (Button) findViewById(R.id.id_domwrite);
Button pullReadBtn = (Button) findViewById(R.id.id_pullread);
Button pullWriteBtn = (Button) findViewById(R.id.id_pullwrite);
mText = (TextView) findViewById(R.id.id_text);
saxReadBtn.setOnClickListener(this);
saxWriteBtn.setOnClickListener(this);
domReadBtn.setOnClickListener(this);
domWriteBtn.setOnClickListener(this);
pullReadBtn.setOnClickListener(this);
pullWriteBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
InputStream inputStream;
try {
inputStream = getAssets().open("id.xml");
switch (v.getId()) {
case R.id.id_saxread:
parseFactory = new SAXParserTool();
parseFactory.readXML(inputStream);
showBookList(parseFactory.getBookList());
break;
case R.id.id_saxwrite:
parseFactory.setBookList(parseFactory.getBookList());
parseFactory.writeXML(Environment.getExternalStorageDirectory().getPath()+"/id.xml");
break;
case R.id.id_domread:
parseFactory = new DOMParserTool();
parseFactory.readXML(inputStream);
showBookList(parseFactory.getBookList());
break;
case R.id.id_domwrite:
parseFactory.writeXML(Environment.getExternalStorageDirectory().getPath()+"/id2.xml");
break;
case R.id.id_pullread:
parseFactory = new PULLParserTool();
parseFactory.readXML(inputStream);
showBookList(parseFactory.getBookList());
break;
case R.id.id_pullwrite:
parseFactory.writeXML(Environment.getExternalStorageDirectory().getPath()+"/id3.xml");
break;
default:
break;
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
private void showBookList(List<Book> bookList) {
StringBuilder builder = new StringBuilder();
for (Book book:bookList) {
builder.append(book.toString());
builder.append("\n");
}
mText.setText(builder.toString());
}
}
解析實(shí)現(xiàn)的主布局
activity_main.xml
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:id="@+id/id_saxread"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SAXParserRead" />
<Button
android:id="@+id/id_saxwrite"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SAXParserWrite" />
<Button
android:id="@+id/id_domread"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="DOMParserRead" />
<Button
android:id="@+id/id_domwrite"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="DOMParserWrite" />
<Button
android:id="@+id/id_pullread"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="PULLParserRead" />
<Button
android:id="@+id/id_pullwrite"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="PULLParserWrite" />
<TextView
android:id="@+id/id_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
效果圖

參考文章
http://www.fzitv.net/article/97247.htm
http://www.fzitv.net/article/97376.htm
PS:這里再為大家提供幾款關(guān)于xml操作的在線工具供大家參考使用:
在線XML/JSON互相轉(zhuǎn)換工具:
http://tools.jb51.net/code/xmljson
在線格式化XML/在線壓縮XML:
http://tools.jb51.net/code/xmlformat
XML在線壓縮/格式化工具:
http://tools.jb51.net/code/xml_format_compress
XML代碼在線格式化美化工具:
http://tools.jb51.net/code/xmlcodeformat
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Android操作XML數(shù)據(jù)技巧總結(jié)》、《Android編程之a(chǎn)ctivity操作技巧總結(jié)》、《Android資源操作技巧匯總》、《Android文件操作技巧匯總》、《Android開(kāi)發(fā)入門(mén)與進(jìn)階教程》、《Android視圖View技巧總結(jié)》及《Android控件用法總結(jié)》
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
- 基于Android XML解析與保存的實(shí)現(xiàn)
- android layout XML解析錯(cuò)誤的解決方法
- Android中使用sax解析xml文件的方法
- Android中使用PULL方式解析XML文件深入介紹
- Android 解析XML 文件的四種方法總結(jié)
- Android 使用Pull方法解析XML文件的方法
- Android提高之XML解析與生成實(shí)例詳解
- Android開(kāi)發(fā)之XML文件解析的使用
- Android中使用pull解析器操作xml文件的解決辦法
- android編程之XML文件解析方法詳解(附源碼)
- Android使用Pull解析器解析xml文件的實(shí)現(xiàn)代碼
相關(guān)文章
Android 版本、權(quán)限適配相關(guān)總結(jié)
針對(duì) Android 6.0 (API 23)已以上版本,Google 增強(qiáng)全新的權(quán)限,應(yīng)用程序在使用敏感權(quán)限(如拍照、查閱聯(lián)系人或存儲(chǔ))時(shí)需要先征求用戶(hù)必須贏得用戶(hù)同意。2021-05-05
android USB如何修改VID具體實(shí)現(xiàn)
在android 設(shè)備的Linux 內(nèi)核中把 USB 驅(qū)動(dòng)的 PID VID 修改以后,也許之前的adb工具就不能識(shí)別設(shè)備了,會(huì)打印出"device not found"的提示2013-06-06
Android?Studio實(shí)現(xiàn)彈窗設(shè)置
這篇文章主要為大家詳細(xì)介紹了Android?Studio實(shí)現(xiàn)彈窗設(shè)置,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android 實(shí)現(xiàn)IOS選擇拍照相冊(cè)底部彈出的實(shí)例
這篇文章主要介紹了Android 實(shí)現(xiàn)IOS選擇拍照相冊(cè)底部彈出的實(shí)例的相關(guān)資料,這里提供了實(shí)現(xiàn)效果圖及實(shí)現(xiàn)代碼,需要的朋友可以參考下2017-07-07
ubuntu上在androidstudio中啟動(dòng)emulator閃退的解決方法
在AndrodStudio中點(diǎn)擊模擬器的啟動(dòng)按鈕后,模擬器界面彈出后,又立刻閃退。下面通過(guò)本文給大家分享ubuntu上在androidstudio中啟動(dòng)emulator閃退的解決方法,一起看看吧2017-07-07
詳解Android過(guò)濾emoji表情正則表達(dá)式
這篇文章主要介紹了Android過(guò)濾emoji表情正則表達(dá)式,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06
Android下通過(guò)httpClient發(fā)送GET和POST請(qǐng)求的實(shí)例代碼
這篇文章介紹了Android下通過(guò)httpClient發(fā)送GET和POST請(qǐng)求的實(shí)例代碼,有需要的朋友可以參考一下2013-08-08
完全解析Android多線程中線程池ThreadPool的原理和使用
本篇文章給大家通過(guò)原理和實(shí)例詳細(xì)講述了Android多線程中線程池ThreadPool的原理和使用,對(duì)此有興趣的朋友可以跟著參考學(xué)習(xí)下。2018-04-04
RecyclerView實(shí)現(xiàn)列表倒計(jì)時(shí)
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)列表倒計(jì)時(shí),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09

