java的xml读写

本文详细介绍了Java中XML的读写方法,包括Dom、SAX、JDom和Dom4j四种方式,并提供了每种方式的读写操作实例。同时,对这四种方式的性能进行了对比,SAX速度最快,但不支持回读。Dom4j因其简洁的代码和良好的性能在实际应用中常见,而JDom和Dom的性能相近,Dom稍优。

xml读取的基本上都是获取一个xml文件的输入流作为参数,通过一些类方法返回一个document对象(sax读取除外),接下下来的是事情就简单了;xml的写入是利用设置好输出格式的transformer转换器将document对象和xml文件的输出流作为参数生成一个xml文档。采用的测试xml是一个包含全球所有一级城市的xml文档,按照读取所有国家、有州就不读城市(中国除外)的逻辑那么整个文档有3512条可用数据。源码下载

一、采用Dom方式读写

Dom方式读写都是利用的DocumentBuilder对象来读取xml文件或创建document对象的,将创DocumentBuilder对象的代码放入一个方法中。

	/**
	 * @Decription TODO 获取DocumentBuilder对象
	 * @date 2016年10月12日 下午8:29:46
	 * @return
	 */
	public  DocumentBuilder getDocumentBuilder(){
		// 获得DocumentBuilderFactory对象
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder db =null;
		try {
			 db= factory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
		return db;
	}
1、Dom读

一次加载所有节点(xml文件过大可能就比较慢的),能保存xml的结构(更方便修改,整棵树都在内存中)。利用DocumentBuilder对象的parse()方法加载xml文档。
	/**
	 * @Decription TODO
	 *  有些小国家是没其他城市的,所以没有子节点.
	 * 	如果有就获取所有一级城市节点:国外有些国家是没有州的state,那么这些国家的一级城市就是city节点的属性值.
	 *	中华人民共和国全部的省份城市都要保存
	 * @date 2016年10月11日 下午2:46:25
	 */
	 public  void  domParseTest() {
		
		try {
			// 获得DocumentBuilder对象
			DocumentBuilder builder = this.getDocumentBuilder();
			// 通过xml文件输入流获取xml的document对象,parse有输入流的重载方法。这里我用的是源文件路经作为参数(懒^_^)
			Document document = builder.parse("LocList.xml");
			// 获取所有国家节点
			NodeList countrys = document.getElementsByTagName("CountryRegion");
			//要保存到数据库的数据题条目的计数器
			int count = 0;
			//遍历所有CountryRegion节点
			for (int j = 0; j < countrys.getLength(); j++) {
				String countryName = "";
				String countryCode = "";
				//获取当前CountryRegion节点
				Element country = (Element) countrys.item(j);
				//判断是否拥有属性
				if(country.hasAttributes()){
					//这是一种获取属性值得方式,当属性较多时可用遍历去获取,下面有遍历获取的的方法
					countryName = country.getAttribute("Name");
					countryCode = country.getAttribute("Code");
					System.out.println("国家:" + countryName + "  代码:" + countryCode);
					count++;
				}
				//判断当前CountryRegion节点是否拥有子节点
				if (country.hasChildNodes()) {
					//获取CountryRegion节点的子节点
					NodeList states = country.getChildNodes();
					//遍历所有CountryRegion节点下的所有State节点
					for (int k = 0; k < states.getLength(); k++) {
						//缓存city或state节点的name属性值
						String cityName = "";
						//缓存city或state节点的code属性值
						String cityCode = "";
						//中国下面的子节点逻辑(if) 其他(else if 和else) 当elseif和前面的if或elseif同时成立时,执行前面的那一个if或elseif后面的elseif不执行。
						if ("State".equals(states.item(k).getNodeName()) && states.item(k).hasAttributes() && "中华人民共和国".equals(countryName)) {
							if (states.item(k).getNodeType() == Node.ELEMENT_NODE){
								NamedNodeMap stateAttr = states.item(k).getAttributes();
								for (int i = 0; i < stateAttr.getLength(); i++) {
									Attr attr = (Attr) stateAttr.item(i);
									if ("Name".equals(attr.getNodeName())) {
										cityName = attr.getNodeValue();
									} else {
										cityCode = attr.getNodeValue();
									}
								}
								System.out.println(countryName + "的省份: " + cityName + "  代码:" + cityCode);
								count++;
								if (states.item(k).hasChildNodes()){
									NodeList cityList = states.item(k).getChildNodes();
									//将省份保留下来
									String curState = cityName;
									for (int city = 0; city < cityList.getLength(); city++) {
										if (cityList.item(city).hasAttributes()){
											NamedNodeMap cityAttr = cityList.item(city).getAttributes();
											for (int i = 0; i < cityAttr.getLength(); i++) {
												Attr attr = (Attr) cityAttr.item(i);
												if ("Name".equals(attr.getNodeName())) {
													cityName = attr.getNodeValue();
												} else {
													cityCode = attr.getNodeValue();
												}
											}
											System.out.println(countryName + curState + "的城市或地区: " + cityName + "  代码:" + cityCode);
											count++;
										}
									}
								}
							}
							
						}else if("State".equals(states.item(k).getNodeName()) && states.item(k).hasAttributes() ){
							NamedNodeMap cityAttr = states.item(k).getAttributes();
							for (int i = 0; i < cityAttr.getLength(); i++) {
								Attr attr = (Attr) cityAttr.item(i);
								if ("Name".equals(attr.getNodeName())) {
									cityName = attr.getNodeValue();
								} else {
									cityCode = attr.getNodeValue();
								}
							}
							System.out.println(countryName + "的一级城市: " + cityName + "  代码:" + cityCode);
							count++;
						}else {
							/**
							 * 前面是没有属性的state节点时System.out.println(states.item(k).getNodeName());输出下列内容:
							 * #text  TEXT_NODE
							 * State  ELEMENT_NODE
							 * #text
							 * 所以要加一个判断
							 **/
							if (states.item(k).getNodeType() == Node.ELEMENT_NODE){
								if(states.item(k).hasChildNodes()){
									NodeList cityList = states.item(k).getChildNodes();
									for (int city = 0; city < cityList.getLength(); city++) {
										if (cityList.item(city).hasAttributes()){
											NamedNodeMap cityAttr = cityList.item(city).getAttributes();
											for (int i = 0; i < cityAttr.getLength(); i++) {
												Attr attr = (Attr) cityAttr.item(i);
												if ("Name".equals(attr.getNodeName())) {
													cityName = attr.getNodeValue();
												} else {
													cityCode = attr.getNodeValue();
												}
											}
											System.out.println(countryName + "的一级城市: " + cityName + "  代码:" + cityCode);
											count++;
										}
									}
								}else{
									NamedNodeMap cityAttr = states.item(k).getAttributes();
									for (int i = 0; i < cityAttr.getLength(); i++) {
										Attr attr = (Attr) cityAttr.item(i);
										if ("Name".equals(attr.getNodeName())) {
											cityName = attr.getNodeValue();
										} else {
											cityCode = attr.getNodeValue();
										}
									}
									System.out.println(countryName + "的一级城市: " + cityName + "  代码:" + cityCode);
									count++;
								}
								
							}
						}
					}
				} else {
					System.out.println(countryName + "是个小国家,没有一级城市!");
				}
			}
			System.out.println("一共有:" + count + "的数据!");
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
2、Dom写

利用DocumentBuilder对象创建document对象,Transformer对象生成xml文档。

/**
	  * dom生成xml文件
	  */
	public void domCreateXml(){
		//创建DocumentBuilder对象
		DocumentBuilder db = this.getDocumentBuilder();
		//生成一个Dom树
		Document document =	db.newDocument();
		//去掉standalone="no"声明,说明只是一个简单的xml,没有特殊DTD(document type definition文档类型定义)规范
		document.setXmlStandalone(true);
		//创建Location根节点
		Element rootElement = document.createElement("Location");
		//创建CountryRegion节点
		Element country = document.createElement("CountryRegion");
		country.setAttribute("Name", "中国");
		country.setAttribute("Code", "1");
		//创建State节点
		Element state = document.createElement("State");
		state.setAttribute("Name", "四川");
		state.setAttribute("Code", "22");
		//创建city节点
		Element city = document.createElement("City");
		city.setAttribute("Name", "成都");
		city.setAttribute("Code", "cd");
		//将city是state下的子节点,将city加入到state中
		state.appendChild(city);
		//将state是country下的子节点,将state加入到country中
		country.appendChild(state);
		//将country是Location下的子节点,将state加入到country中
		rootElement.appendChild(country);
		
		//将包含了子节点的rootElement添加到document中
		document.appendChild(rootElement);
		//实例化工厂类,工厂类不能使用new关键字实例化创建对象
		TransformerFactory transFactory = TransformerFactory.newInstance();
		try {
			//创建transformer对象
			Transformer transformer = transFactory.newTransformer();
			//设置换行
			transformer.setOutputProperty(OutputKeys.INDENT, "Yes");
			//构造转换,参数都是抽象类,要用的却是更具体的一些类,这些的类的命名有一些规律的。
			transformer.transform(new DOMSource(document), new StreamResult("LocListDom.xml"));
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
	}

二、采用SAX方式读写

1、SAX读

事件驱动,逐行读取,通过重写DefaultHandler来获取解析的场景。

/**
	 * @Decription TODO
	 * @date 2016年10月12日 下午7:33:26
	 */
	public  void saxParseTest(){
		//SAXParserFactory工厂类对象
		SAXParserFactory factory  =	SAXParserFactory.newInstance();
		InputStream in = null;
		try {
			//利用工厂类创建SAXParser对象
			SAXParser parser = factory.newSAXParser();
			//创建文件输入流
			in = new FileInputStream("LocList.xml");
			//解析
			parser.parse(in, new SaxHandler());
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				if(in != null)
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

通过继承DefaultHandler重写4个方法获取解析状态

package my.sax.practice;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SaxHandler extends DefaultHandler{
		//缓存CountryRegion的name属性值
		String countryName = "";
		//缓存CountryRegion的code属性值
		String countryCode = "";
		//缓存city或state节点的name属性值
		String stateName = "";
		//缓存city或state节点的code属性值
		String stateCode = "";
		/**
		 * xml解析开始
		 */
		@Override
		public void startDocument() throws SAXException {
			super.startDocument();
			System.out.println("sax的xml解析开始");
		}
		/**
		 * 解析节点开始
		 */
		@Override
		public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
			super.startElement(uri, localName, qName, attributes);
			//attributes!=null判断不行
			if(attributes.getLength() > 0){
				switch (qName) {
				case "CountryRegion":
					countryName =  attributes.getValue("Name");
					countryCode =  attributes.getValue("Code");
					System.out.println("国家:" + countryName + "  代码:" + countryCode);
					break;
				case "State":
					stateName =  attributes.getValue("Name");
					stateCode = attributes.getValue("Code");
					System.out.println(countryName + "的一级城市: " + stateName + "  代码:" + stateCode);
					break;	
				case "City":
					String cityName = attributes.getValue("Name");
					String cityCode = attributes.getValue("Code");
					if("".equals(stateName) && "".equals(stateCode)){
						System.out.println(countryName + "的一级城市: " + cityName + "  代码:" + cityCode);
					}else{
						System.out.println(countryName + stateName + "的城市或地区: " + cityName + "  代码:" + cityCode);
					}
					break;	
				default:
					break;
				}
			}else{
				if(qName.equals("State")){
					stateName = "";
					stateCode = "";
				}
			}
		}
		/**
		 * 解析节点结束
		 */
		@Override
		public void endElement(String uri, String localName, String qName) throws SAXException {
			super.endElement(uri, localName, qName);
		}
		/**
		 * xml解析结束
		 */
		@Override
		public void endDocument() throws SAXException {
			super.endDocument();
			System.out.println("sax的xml解析结束");
		}
}

2、SAX写

/**
	 * @Decription TODO sax方法创建一个xml文档
	 * @date 2016年10月12日 下午8:37:26
	 */
	public void saxCreateXml(){
		//1、创建一个SAXTransformerFactory一个对象
		SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
		OutputStream in =null;
		try {
			//2、通过SAXTransformerFactory创建一个TransformerHandler对象
			TransformerHandler handler = sf.newTransformerHandler();
			//3、通过TransformerHandler对象获取Transformer对象(用于设置xml输出的样式和头)
			Transformer transformer = handler.getTransformer();
			//设置没有其他的DTD(Document Type Defination 文档类型定义)规范
			transformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
			//设置编码格式,显式的显示在<?xml version="1.0" ?>中
			transformer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
			//设置换行
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			//文件输出
			File file =new File("LocListSax.xml");
			//确保file是存在的
			if(!file.exists()){
				if(!file.createNewFile()){
					throw new FileNotFoundException("文件创建失败!");
				}
			}
			//4、创建输出流OutputStream对象
			in = new FileOutputStream(file);
			//5、创建流Result对象
			Result result = new StreamResult(in);
			//6、关联result,此时有了生成元素的方法(handler提供的方法)和装元素的容器(result对象)
			handler.setResult(result);
			//打开文档
			handler.startDocument();
			//属性设置
			AttributesImpl attr = new AttributesImpl();
			//开始创建元素
			handler.startElement("", "", "Location", attr);
			attr.addAttribute("", "", "Name", "", "阿尔巴尼亚");
			attr.addAttribute("", "", "Code", "", "DZA");
			handler.startElement("", "", "CountryRegion", attr);
			attr.clear();
			attr.addAttribute("", "", "Name", "", "阿德拉尔");
			attr.addAttribute("", "", "Code", "", "ADR");
			handler.startElement("", "", "State", attr);
			handler.endElement("", "", "State");
			handler.endElement("", "", "CountryRegion");
			//结束元素创建
			handler.endElement("", "", "Location");
			//关闭文档
			handler.endDocument();
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}	finally{
				try {
					//关闭流, 习惯不好,老是不喜欢关闭流
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
				
	}

三、采用JDom方式读写

1、JDom读

public static void jdomParseTest(){
		//创建SAXBuilder对象
		SAXBuilder builder = new SAXBuilder();
		//创建InputStream对象并初始化变量null
		InputStream in = null ;
		//缓存CountryRegion的name属性值
		String countryName = "";
		//缓存CountryRegion的code属性值
		String countryCode = "";
		//缓存city或state节点的name属性值
		String stateName = "";
		//缓存city或state节点的code属性值
		String stateCode = "";
		//初始化字符输入流
		InputStreamReader reader =null;
		try {
			//获得文件的输入流
			in =new FileInputStream("LocList.xml");
			//给设置字符输入流设置编码格式
			reader = new InputStreamReader(in, "UTF-8");
			//通过SAXBuilder对象的build方法获取xml的Document对象
			//报错:前言中不允许有内容,解决办法:使用notepad++ utf-8保存一下
			Document document = builder.build(reader);
			//获取根节点Location
			Element rootElement = document.getRootElement();
			//获取根节点Location的子节点
			List<Element> coutryList = rootElement.getChildren();
			//遍历子节点
			for(Element country : coutryList){
				if (country.hasAttributes()){
					countryName = country.getAttributeValue("Name");
					countryCode = country.getAttributeValue("Code");
					System.out.println("国家:" + countryName + "  代码:" + countryCode);
					//获取country的子节点state
					List<Element> stateList = country.getChildren();
					//遍历state
					for (Element state : stateList){
						//判断是否拥有属性外国的state节点都是没有属性的,有
						if(state.hasAttributes()){
							stateName = state.getAttributeValue("Name");
							stateCode = state.getAttributeValue("Code");
							//当是中国时才遍历state下的子节点
							if("中华人民共和国".equals(countryName)){
								//获取state的子节点city
								List<Element> cityList = state.getChildren();
								//遍历city
								for (Element city : cityList){
									if(city.hasAttributes()){
										String cityName = city.getAttributeValue("Name");
										String cityCode = city.getAttributeValue("Code");
										System.out.println(countryName + stateName + "的城市或地区: " + cityName + "  代码:" + cityCode);
									}
								}
							}else{
								System.out.println(countryName + "的一级城市: " + stateName + "  代码:" + stateCode);
							}
						}else{
							//获取state的子节点city
							List<Element> cityList = state.getChildren();
							//遍历city
							for (Element city : cityList){
								if(city.hasAttributes()){
									String cityName = city.getAttributeValue("Name");
									String cityCode = city.getAttributeValue("Code");
									System.out.println(countryName + "的一级城市: " + cityName + "  代码:" + cityCode);
								}
							}
						}
					}
				}
			}
			
		} catch (UnsupportedEncodingException e1) {
			e1.printStackTrace();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			
				try {
					if(in !=null)
					in.close();
					if (reader != null)
					reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

2、JDom写

/**
	 *jdom方法生成xml文件
	 */
	public static void jdomCreateXml(){
		//创建根节点
		Element root = new Element("Location");
		//创建document对象
		Document document = new Document(root);	
		//添加子节点
		Element child = new Element("CountryRegion");
		child.setAttribute("Name", "中国");
		child.setAttribute("Code", "1");
		//添加子节点
		Element subchild = new Element("State");
		subchild.setAttribute("Name", "四川");
		subchild.setAttribute("Code", "sc");
		//添加子节点
		Element subsubchild = new Element("City");
		subsubchild.setAttribute("Name", "成都");
		subsubchild.setAttribute("Code", "cd");
		//添加孙子节点
		subchild.addContent(subsubchild);
		//添加子节点
		child.addContent(subchild);
		//添加节点
		root.addContent(child);
		//创建Format对象,格式化xml
		Format formater =Format.getPrettyFormat();
		//创建XMLOutputter对象
		XMLOutputter outputer = new XMLOutputter(formater);
		//初始化输出流,局部变量必须初始化
		OutputStream out = null;
		//创建xml文件
		File file = new File("LocListJdom.xml");
		try {
			if (!file.exists()){
					if (!file.createNewFile()){
						throw new FileCanNotCreateException();
					}
			}
			//创建输出流
			out = new FileOutputStream(file);
			//XMLOutputter写入
			outputer.output(document, out);
		} catch (IOException e) {
			e.printStackTrace();
			e.printStackTrace();
		} catch (FileCanNotCreateException e) {
			e.printStackTrace();
		} finally{
			//关闭流
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}


四、采用Dom4j方式读写

1、Dom4j读

public static void dom4jParseTest(){
		//缓存CountryRegion的name属性值
		String countryName = "";
		//缓存CountryRegion的code属性值
		String countryCode = "";
		//缓存city或state节点的name属性值
		String stateName = "";
		//缓存city或state节点的code属性值
		String stateCode = "";
		//创建InputStream对象并初始化变量null
		InputStream in = null;
		SAXReader reader = new SAXReader();
		//获得文件的字符输入流
		InputStreamReader isr =null;
		try {
			in =new FileInputStream("LocList.xml");
			//给设置输入流直射编码格式
			isr = new InputStreamReader(in, "UTF-8");
			//使用read方法将输入流加载到SAXBuilder中获得xml的Document对象
			Document document = reader.read(isr);
			//获取根节点Location
			Element rootElement = document.getRootElement();
			//获取根节点Location的子节点
			@SuppressWarnings("unchecked")
			List<Element> coutryList = rootElement.elements();
			//遍历子节点
			for(Element country : coutryList){
				if (country.attributes()!=null && country.attributes().size()>0){
					countryName = country.attributeValue("Name");
					countryCode = country.attributeValue("Code");
					System.out.println("国家:" + countryName + "  代码:" + countryCode);
					//获取country的子节点state
					@SuppressWarnings("unchecked")
					List<Element> stateList = country.elements();
					//遍历state
					for (Element state : stateList){
						//判断是否拥有属性外国的state节点都是没有属性的,有
						if(state.attributes()!=null && state.attributes().size()>0){
							stateName = state.attributeValue("Name");
							stateCode = state.attributeValue("Code");
							//当是中国时才遍历state下的子节点
							if("中华人民共和国".equals(countryName)){
								//获取state的子节点city
								@SuppressWarnings("unchecked")
								List<Element> cityList = state.elements();
								//遍历city
								for (Element city : cityList){
									if(city.attributes()!=null && city.attributes().size()>0){
										String cityName = city.attributeValue("Name");
										String cityCode = city.attributeValue("Code");
										System.out.println(countryName + stateName + "的城市或地区: " + cityName + "  代码:" + cityCode);
									}
								}
							}else{
								System.out.println(countryName + "的一级城市: " + stateName + "  代码:" + stateCode);
							}
						}else{
							//获取state的子节点city
							@SuppressWarnings("unchecked")
							List<Element> cityList = state.elements();
							//遍历city
							for (Element city : cityList){
								if(city.attributes()!=null && city.attributes().size()>0){
									String cityName = city.attributeValue("Name");
									String cityCode = city.attributeValue("Code");
									System.out.println(countryName + "的一级城市: " + cityName + "  代码:" + cityCode);
								}
							}
						}
					}
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}catch (DocumentException e) {
			e.printStackTrace();
		}catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} finally{
			//关闭流
			try {
				if(in != null)
					in.close();
				if (isr != null)
					isr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	}

2、Dom4j写

/**
	 * 生成xml文件
	 */
	public static void Dom4jCreateXml(){
		//初始化输出流
		OutputStream out = null;
		//通过DocumentHelper创建document对象
		Document document = DocumentHelper.createDocument();
		//创建根节点
		Element root = document.addElement("Location");
		//添加子节点
		Element child =root.addElement("CountryRegion");
		child.addAttribute("Name", "中国");
		child.addAttribute("Code", "1");
		//添加子节点
		Element subchild = child.addElement("State");
		subchild.addAttribute("Name", "四川");
		subchild.addAttribute("Code", "sc");
		//添加子节点
		Element subsubchild = subchild.addElement("City");
		subsubchild.addAttribute("Name", "成都");
		subsubchild.addAttribute("Code", "cd");
		//将文件输出
		try {
			//创建输出流
			out = new FileOutputStream("LocListDom4j.xml");
			//createPrettyPrint格式化xml并返回一个OutPutFormat对象
			OutputFormat of = OutputFormat.createPrettyPrint();
			//创建一个XMLWriter对象
			XMLWriter writer = new XMLWriter(out,of);
			writer.write(document);
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

五、四种方式性能对比

四种方法中sax读写都是最快,但是sax没办法往回读的。dom4j是第二最关键的代码量少啊,在实际的应用比较多(spring就是用的dom4j)。jdom和dom都差不多,dom的性能还要好一些(我没测试过几次啊,目测)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值