xml解析工具类

xml是用来描述一组数据的规范,在不同系统交互,或者通信的过程中,我们往往使用到它,所以对于xml解析用的也是比较多的。本文没有依赖任何第三方jar包,就是jdk本身的API完成,支持任意复杂的xml的解析,有自定义handler供实现。

package com.epoint.utility.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.xml.sax.InputSource;

/**
* XML读取工具类,依赖jar包:无
*
* @author komojoemary
* @version [版本号, 2010-11-19]
*/
public class ReadXML
{

    /**
     * 判断依据1:节点文本内容不为空
     */
    public static final String JUDGEMENT_BASIS_TYPE1 = "textContentNotNull";

    /**
     * 判断依据2:节点文本内容为空
     */
    public static final String JUDGEMENT_BASIS_TYPE2 = "textContentNull";

    /**
     * 判断依据3:属性不为空
     */
    public static final String JUDGEMENT_BASIS_TYPE3 = "attributeNotNull";

    /**
     * 判断依据4:属性为空
     */
    public static final String JUDGEMENT_BASIS_TYPE4 = "attributeNull";

    /**
     * 默认的判断依据是节点文本内容不为空
     */
    private String judgementBasis = JUDGEMENT_BASIS_TYPE1;

    /**
     * 是否仅获取一个唯一的节点
     */
    private boolean isOnly = false;

    /**
     * 自定义xml解析接口
     */
    private XmlParserHandler xmlParserHandler = null;

    private boolean refresh = false;

    private String filePath = null;

    public ReadXML() {
    }

    public ReadXML(XmlParserHandler xmlParserHandler) {
        this.xmlParserHandler = xmlParserHandler;
    }

    public ReadXML(String judgementBasis) {
        this.judgementBasis = judgementBasis;
    }

    /************************************* 下面18个是对外公布的接口 ***********************************************/
    /**
     * 根据节点tag从xml文件中解析获取一个满足条件的xml节点的文本值
     *
     * @param filePath
     *            文件路径
     * @param tagName
     *            节点tag
     * @return String
     */
    public String getTextContentByTagFromFile(String filePath, String tagName) {
        this.isOnly = true;
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, filePath, true), tagName, null, XMLNodeList);
        if (XMLNodeList.size() > 0) {
            return XMLNodeList.get(0).getTextValue();
        }
        return null;
    }

    /**
     * 根据节点tag从xml文件中解析获取一个满足条件的xml节点
     *
     * @param filePath
     *            文件路径
     * @param tagName
     *            节点tag
     * @return XMLNode
     */
    public XMLNode getXMLNodeByTagFromFile(String filePath, String tagName) {
        this.isOnly = true;
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, filePath, true), tagName, null, XMLNodeList);
        if (XMLNodeList.size() > 0) {
            return XMLNodeList.get(0);
        }
        return null;
    }

    /**
     * 根据节点文本内容从xml文件中解析获取一个满足条件的xml节点
     *
     * @param filePath
     *            文件路径
     * @param text
     *            节点文本内容
     * @return XMLNode
     */
    public XMLNode getXMLNodeByTextFromFile(String filePath, String text) {
        this.isOnly = true;
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, filePath, true), null, text, XMLNodeList);
        if (XMLNodeList.size() > 0) {
            return XMLNodeList.get(0);
        }
        return null;
    }

    /**
     * 根据节点tag从xml文件中解析获取一组满足条件的xml节点
     *
     * @param filePath
     *            文件路径
     * @param tagName
     *            节点tag
     * @return List<XMLNode>
     */
    public List<XMLNode> getXMLNodeListByTagFromFile(String filePath, String tagName) {
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, filePath, true), tagName, null, XMLNodeList);
        return XMLNodeList;
    }

    /**
     * 根据节点文本内容从xml文件中解析获取一组满足条件的xml节点
     *
     * @param filePath
     *            文件路径
     * @param text
     *            节点文本内容
     * @return List<XMLNode>
     */
    public List<XMLNode> getXMLNodeListByTextFromFile(String filePath, String text) {
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, filePath, true), null, text, XMLNodeList);
        return XMLNodeList;
    }

    /*-----------------------------------------------------------------------------------------------*/
    /**
     * 根据节点tag从xml文件内容中解析获取一个满足条件的xml节点的文本值
     *
     * @param content
     *            xml文件内容
     * @param tagName
     *            节点tag
     * @return String
     */
    public String getTextContentByTagFromStr(String content, String tagName) {
        this.isOnly = true;
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, content, false), tagName, null, XMLNodeList);
        if (XMLNodeList.size() > 0) {
            return XMLNodeList.get(0).getTextValue();
        }
        return null;
    }

    /**
     * 根据节点tag从xml文件内容中解析获取一个满足条件的xml节点
     *
     * @param content
     *            xml文件内容
     * @param tagName
     *            节点tag
     * @return XMLNode
     */
    public XMLNode getXMLNodeByTagFromStr(String content, String tagName) {
        this.isOnly = true;
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, content, false), tagName, null, XMLNodeList);
        if (XMLNodeList.size() > 0) {
            return XMLNodeList.get(0);
        }
        return null;
    }

    /**
     * 根据节点文本内容从xml文件内容中解析获取一个满足条件的xml节点
     *
     * @param content
     *            xml文件内容
     * @param text
     *            节点文本内容
     * @return XMLNode
     */
    public XMLNode getXMLNodeByTextFromStr(String content, String text) {
        this.isOnly = true;
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, content, false), null, text, XMLNodeList);
        if (XMLNodeList.size() > 0) {
            return XMLNodeList.get(0);
        }
        return null;
    }

    /**
     * 根据节点tag从xml文件内容中解析获取一组满足条件的xml节点
     *
     * @param content
     *            xml文件内容
     * @param tagName
     *            节点tag
     * @return List<XMLNode>
     */
    public List<XMLNode> getXMLNodeListByTagFromStr(String content, String tagName) {
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, content, false), tagName, null, XMLNodeList);
        return XMLNodeList;
    }

    /**
     * 根据节点文本内容从xml文件内容中解析获取一组满足条件的xml节点
     *
     * @param content
     *            xml文件内容
     * @param text
     *            节点文本内容
     * @return List<XMLNode>
     */
    public List<XMLNode> getXMLNodeListByTextFromStr(String content, String text) {
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(null, content, false), null, text, XMLNodeList);
        return XMLNodeList;
    }

    /*-----------------------------------------------------------------------------------------------*/
    /**
     * 根据节点tag从xml文件输入流中解析获取一个满足条件的xml节点的文本值
     *
     * @param ins
     *            xml文件输入流
     * @param tagName
     *            节点tag
     * @return String
     */
    public String getTextContentByTagFromInputStream(InputStream ins, String tagName) {
        this.isOnly = true;
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(ins, null, false), tagName, null, XMLNodeList);
        if (XMLNodeList.size() > 0) {
            return XMLNodeList.get(0).getTextValue();
        }
        return null;
    }

    /**
     * 根据节点tag从xml文件输入流中解析获取一个满足条件的xml节点
     *
     * @param ins
     *            xml文件输入流
     * @param tagName
     *            节点tag
     * @return XMLNode
     */
    public XMLNode getXMLNodeByTagFromInputStream(InputStream ins, String tagName) {
        this.isOnly = true;
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(ins, null, false), tagName, null, XMLNodeList);
        if (XMLNodeList.size() > 0) {
            return XMLNodeList.get(0);
        }
        return null;
    }

    /**
     * 根据节点文本内容从xml文件输入流中解析获取一个满足条件的xml节点
     *
     * @param ins
     *            xml文件输入流
     * @param text
     *            节点文本内容
     * @return XMLNode
     */
    public XMLNode getXMLNodeByTextFromInputStream(InputStream ins, String text) {
        this.isOnly = true;
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(ins, null, false), null, text, XMLNodeList);
        if (XMLNodeList.size() > 0) {
            return XMLNodeList.get(0);
        }
        return null;
    }

    /**
     * 根据节点tag从xml文件输入流中解析获取一组满足条件的xml节点
     *
     * @param ins
     *            xml文件输入流
     * @param tagName
     *            节点tag
     * @return List<XMLNode>
     */
    public List<XMLNode> getXMLNodeListByTagFromInputStream(InputStream ins, String tagName) {
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(ins, null, false), tagName, null, XMLNodeList);
        return XMLNodeList;
    }

    /**
     * 根据节点文本内容从xml文件输入流中解析获取一组满足条件的xml节点
     *
     * @param ins
     *            xml文件输入流
     * @param text
     *            节点文本内容
     * @return List<XMLNode>
     */
    public List<XMLNode> getXMLNodeListByTextFromInputStream(InputStream ins, String text) {
        List<XMLNode> XMLNodeList = new ArrayList<XMLNode>();
        getXMLNode(getDocument(ins, null, false), null, text, XMLNodeList);
        return XMLNodeList;
    }

    /*-----------------------------------------------------------------------------------------------*/
    /**
     * 从一个xml文件中进行解析(针对于自定义解析接口的情况,实现比较复杂的xml解析逻辑)
     *
     * @param filePath
     *            文件路径
     */
    public void parserXmlFromFile(String filePath) {
        getXMLNode(getDocument(null, filePath, true), null, null, null);
    }

    /**
     * 从一个xml文件内容中进行解析(针对于自定义解析接口的情况,实现比较复杂的xml解析逻辑)
     *
     * @param filePath
     *            文件内容
     */
    public void parserXmlFromStr(String content) {
        getXMLNode(getDocument(null, content, false), null, null, null);
    }

    /**
     * 从一个xml文件输入流中进行解析(针对于自定义解析接口的情况,实现比较复杂的xml解析逻辑)
     *
     * @param ins
     *            文件输入流
     */
    public void parserXmlFromInputStream(InputStream ins) {
        getXMLNode(getDocument(ins, null, false), null, null, null);
    }

    /************************************* 上面18个是对外公布的接口 ***********************************************/
    /**
     * 获取一个Document
     *
     * @param ins
     *            输入流
     * @param pathOrContent
     *            文件路径or文件内容
     * @param file
     *            是否是文件
     * @return Document
     */
    private static Document getDocument(InputStream ins, String pathOrContent, boolean file) {
        // 创建新的输入源SAX 解析器
        SAXBuilder sb = new SAXBuilder();
        // 创建一个dom对象
        Document doc = null;
        FileInputStream fis = null;
        try {
            // 如果输入流不为空,那么直接构造
            if (ins != null) {
                doc = sb.build(ins);
            }
            else {
                InputSource source = null;
                // 如果是文件,那么先构造一个文件
                if (file) {
                    File newfile = new File(pathOrContent);
                    fis = new FileInputStream(newfile);
                    source = new InputSource(fis);
                }
                // 如果是内容,那么构造一个输入流
                else {
                    StringReader reader = new StringReader(pathOrContent);
                    source = new InputSource(reader);
                }
                // 通过输入源构造一个Document
                doc = sb.build(source);
            }
        }
        catch (JDOMException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if (fis != null) {
                // 2011年3月14(关闭流)
                try {
                    fis.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
       
//            Transformer transformer;
//            try {
//                transformer = TransformerFactory.newInstance().newTransformer(new JDOMSource(doc));
//                transformer.getOutputProperty("encoding");
//            }
//            catch (TransformerConfigurationException e) {
//                // TODO Auto-generated catch block
//                e.printStackTrace();
//            }
//            catch (TransformerFactoryConfigurationError e) {
//                // TODO Auto-generated catch block
//                e.printStackTrace();
//            }
//            XSLTransformer transformer=new XSLTransformer(doc);
           
        return doc;
    }

    public void getXMLNode(Document doc, String tagName, String text, List<XMLNode> XMLNodeList) {
        // 取的根元素
        Element root = doc.getRootElement();
        // 递归整个dom tree
        recureDocument(root, tagName, text, XMLNodeList);
        // 如果需要更新
        if (refresh) {
            refreshXml(doc, filePath);
        }
    }

    public static void refreshXml(Document doc, String filePath) {
        Format f = Format.getPrettyFormat();   
        f.setEncoding("utf-8"); 
        f.setLineSeparator("\r\n");
        XMLOutputter outputter = new XMLOutputter(f);
        try {
            FileWriter writer = new FileWriter(filePath);
            outputter.output(doc, writer);
            writer.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
      

//        try {
//            Transformer transformer = TransformerFactory.newInstance().newTransformer();
//            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
//            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
//            transformer.transform(new JDOMSource(doc), new StreamResult(filePath));
//        }
//        catch (TransformerException e) {
//            e.printStackTrace();
//        }
//        catch (TransformerFactoryConfigurationError e) {
//            e.printStackTrace();
//        }
       
       
//
//        TransformerFactory tf = TransformerFactory.newInstance();
//        // 此实例可以用于处理来自不同源的 XML,并将转换输出写入各种接收器。
//        Transformer transformer;
//        OutputStreamWriter pw = null;
//        try {
//            transformer = tf.newTransformer();
//            // 创建带有 DOM 节点的新输入源
//            JDOMSource source = new JDOMSource(doc);
//            // 设置转换中实际的输出属性
//            transformer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
//            // 设置转换中实际的输出属性
//            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
//            // 编码方式
//            pw = new OutputStreamWriter(new FileOutputStream(filePath), "UTF-8");
//            // 从字节流构造 StreamResult
//            StreamResult result = new StreamResult(pw);
//            // 充当转换结果的持有者,可以为
//            // XML、纯文本、HTML
//            // 或某些其他格式的标记
//            // 将 XML Source 转换为 Result
//            transformer.transform(source, result);
//            // 关闭流
//        }
//        catch (Exception ex) {
//            Logger.getLogger(WriteXML.class.getName()).log(Level.SEVERE, null, ex);
//        }
//        finally {
//            if (pw != null) {
//                try {
//                    pw.close();
//                }
//                catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//        }
   
    }

    /**
     * 递归Document树:找出满足条件的节点
     *
     * @param nodeEl
     *            当前节点
     * @param tagName
     *            标签名字
     * @param text
     *            文本内容
     * @param XMLNodeList
     *            存储满足条件节点的容器
     * @return boolean 是否结束
     */
    @SuppressWarnings("unchecked")
    public boolean recureDocument(Element nodeEl, String tagName, String text, List<XMLNode> XMLNodeList) {
        // 判断是否到了解析终止点
        boolean breakPoint = checkBreakPoint(nodeEl, tagName, text);
        // 如果到了终止点
        if (breakPoint) {
            // 自定义接口的直接终止,否则根据解析过滤规则判断是否能够终止
            if (xmlParserHandler != null || endDocument(nodeEl, XMLNodeList)) {
                return true;
            }
        }
        // 没有的话继续递归后续子节点
        else {
            // 得到根元素所有子元素的集合
            List<Element> nodeList = nodeEl.getChildren();
            if (nodeList != null && nodeList.size() > 0) {
                for (Element ele : nodeList) {
                    if (recureDocument(ele, tagName, text, XMLNodeList)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 检测是否到了解析终止点
     *
     * @param nodeEl
     *            当前节点
     * @param tagName
     *            标签名字
     * @param text
     *            文本内容
     * @return 是否终止点
     */
    private boolean checkBreakPoint(Element nodeEl, String tagName, String text) {
        boolean breakPoint = false;
        // 自定义xml解析接口
        if (xmlParserHandler != null) {
            breakPoint = xmlParserHandler.parserElementNode(nodeEl);
        }
        // 查询某些特殊节点
        else {
            boolean tag = tagName == null ? false : true;
            // 查询节点的方式,tag or text,根据名字匹配规则判断是否找到了节点
            if (tag && tagName.equals(nodeEl.getName()) || !tag && text.equals(nodeEl.getText())) {
                breakPoint = true;
            }
        }
        return breakPoint;
    }

    /**
     * 添加一个XMLNode,并且判断是否已经完成
     *
     * @param root
     *            Element节点
     * @return XMLNode
     */
    @SuppressWarnings("unchecked")
    private boolean endDocument(Element root, List<XMLNode> XMLNodeList) {
        boolean end = false;
        XMLNode node = new XMLNode(root.getName(), root.getText());
        List<Attribute> list = root.getAttributes();
        if (list != null && list.size() > 0) {
            for (Attribute attribute : list) {
                node.getAttribute().add(new XMLNode(attribute.getName(), attribute.getValue()));
            }
        }
        // 是否要获取唯一节点
        if (isOnly) {
            if (judgementBasis.equals(JUDGEMENT_BASIS_TYPE1)) {
                if (root.getText() != null && !root.getText().equals("")) {
                    XMLNodeList.add(node);
                    end = true;
                }
            }
            else if (judgementBasis.equals(JUDGEMENT_BASIS_TYPE2)) {
                if (root.getText() == null || root.getText().equals("")) {
                    XMLNodeList.add(node);
                    end = true;
                }
            }
            else if (judgementBasis.equals(JUDGEMENT_BASIS_TYPE3)) {
                if (root.getAttributes() != null && root.getAttributes().size() > 0) {
                    XMLNodeList.add(node);
                    end = true;
                }
            }
            else if (judgementBasis.equals(JUDGEMENT_BASIS_TYPE4)) {
                if (root.getAttributes() == null || root.getAttributes().size() == 0) {
                    XMLNodeList.add(node);
                    end = true;
                }
            }

        }
        else {
            XMLNodeList.add(node);
        }
        return end;
    }

    public void setRefresh(boolean refresh) {
        this.refresh = refresh;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    // public static void main(String[] args) {
    // System.out.println(new
    // ReadXML(ReadXML.JUDGEMENT_BASIS_TYPE3).getTextContentByTagFromFile("D:\\test.xml",
    // "第一层节点"));
    // System.out.println(new
    // ReadXML().getTextContentByTagFromFile("D:\\test.xml", "第二层节点"));
    // XMLNode node = new
    // ReadXML(ReadXML.JUDGEMENT_BASIS_TYPE3).getXMLNodeByTagFromFile("D:\\test.xml",
    // "第一层节点");
    // System.out.println(node.getAttribute().get(0).getTagName());
    // System.out.println(node.getAttribute().get(0).getTextValue());

    // List<XMLNode> list = new ReadXML().getXMLNodeListByTagFromFile(
    // "D:\\test.xml", "第二层节点");
    // if (list != null && list.size() > 0) {
    // for (XMLNode nodeItem : list) {
    // System.out.println("节点名字:" + nodeItem.getTagName());
    // System.out.println("节点值:" + nodeItem.getTextValue());
    // List<XMLNode> attribute = nodeItem.getAttribute();
    // for (XMLNode item : attribute) {
    // System.out.println("属性名字:" + item.getTagName());
    // System.out.println("属性值:" + item.getTextValue());
    // }
    // }
    // }
    // }
}

package com.epoint.utility.xml;

import java.util.ArrayList;
import java.util.List;

/**
* XML节点对象
*
* @author komojoemary
* @version [版本号, 2010-11-17]
*/
public class XMLNode
{
    /**
     * 标签名字
     */
    private String tagName = null;

    /**
     * 文本值
     */
    private String textValue = null;

    /**
     * 属性列表
     */
    private List<XMLNode> attribute;

    /**
     * 子节点列表
     */
    private List<XMLNode> children;

    public XMLNode() {
    }

    /**
     * 构造函数
     *
     * @param tagName
     *            标签名字
     * @param textValue
     *            标签值
     */
    public XMLNode(String tagName, String textValue) {
        super();
        this.tagName = tagName;
        this.textValue = textValue;
    }

    /**
     * 构造函数
     *
     * @param tagName
     *            标签名字
     * @param textValue
     *            标签值
     * @param attributeName
     *            属性名字
     * @param attributeValue
     *            属性值
     */
    public XMLNode(String tagName, String textValue, String attributeName, String attributeValue) {
        super();
        this.tagName = tagName;
        this.textValue = textValue;
        if (attributeName != null && !attributeName.equals("")) {
            getAttribute().add(new XMLNode(attributeName, attributeValue));
        }
    }

    /**
     * 添加属性
     *
     * @param attribute
     *            属性节点对象
     */
    public void addAttribute(XMLNode attribute) {
        getAttribute().add(attribute);
    }

    /**
     * 添加子节点
     *
     * @param childNode
     *            子节点对象
     */
    public void addChildNode(XMLNode childNode) {
        getChildren().add(childNode);
    }

    /**
     * 添加子节点(返回添加的子节点对象)
     *
     * @param tagName
     *            标签名字
     * @param textValue
     *            标签值
     * @return XMLNode
     */
    public XMLNode addChildNode(String tagName, String textValue) {
        XMLNode node = new XMLNode(tagName, textValue);
        getChildren().add(node);
        return node;
    }

    public String getTagName() {
        return tagName;
    }

    public void setTagName(String tagName) {
        this.tagName = tagName;
    }

    public String getTextValue() {
        return textValue;
    }

    public void setTextValue(String textValue) {
        this.textValue = textValue;
    }

    public List<XMLNode> getAttribute() {
        if (attribute == null) {
            attribute = new ArrayList<XMLNode>();
        }
        return attribute;
    }

    public void setAttribute(List<XMLNode> attribute) {
        this.attribute = attribute;
    }

    public List<XMLNode> getChildren() {
        if (children == null) {
            children = new ArrayList<XMLNode>();
        }
        return children;
    }

    public void setChildren(List<XMLNode> children) {
        this.children = children;
    }

    /**
     * 是否叶子节点
     *
     * @return boolean
     */
    public boolean isLeaf() {
        return getChildren().size() == 0 ? true : false;
    }

}

 原文出处 http://www.cnblogs.com/komojoemary/archive/2011/11/06/xmlParser.html

package com.epoint.utility.xml;

import org.jdom.Element;

/**
* xml解析自定义接口,用于比较复杂的xml解析,实现自己的特定业务逻辑
*
* @作者 komojoemary
* @version [版本号, 2011-7-4]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public interface XmlParserHandler
{
    /**
     * 解析xml dom tree 节点
     *
     * @param nodeEl
     *            某个节点
     * @return 是否结束解析
     */
    public boolean parserElementNode(Element nodeEl);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值