Xml解析获取标记之间的数据以及父信息

时间:2013-06-28 19:23:44

标签: java xml

我正在尝试编写一个通用的xml解析器来解析所有xml标记,并将数据及其值作为键值对获取到地图中。 示例xml:

<?xml version="1.0"?>
<company>
    <staff>
        <firstname>Kevin</firstname>
        <lastname>Gay</lastname>
        <salary>50000</salary>
    </staff>
</company>

输出如下:         NodeName:[公司]价值:[

        Kevin
        Gay
        50000

]
NodeName:[staff] Value:[
    Kevin
    Gay
    50000
]
NodeName:[firstname] Value:[Kevin]
NodeName:[lastname] Value:[Gay]
NodeName:[salary] Value:[50000]

我的代码如下:

    final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    final DocumentBuilder db = dbf.newDocumentBuilder();
    final ByteArrayInputStream bis = new ByteArrayInputStream(xmlString.getBytes());
    //where xmlString is a file read using DataInputStream.
    final Document doc1 = db.parse(bis);
    printElements(doc1);

void printElements(final Document doc)
{
    final NodeList nl = doc.getElementsByTagName("*");
    Node node;

    for (int i = 0; i < nl.getLength(); i++)
    {
        node = nl.item(i);
        System.out.println("NodeName:[" + node.getNodeName() + "] Value:[" + node.getTextContent() + "]");           
    }
}

如何从打印中消除员工和公司属性。 我不想按标签名称使用JAXB或getTags,因为xml标签数据每次都会更改,我正在编写通用的xml解析器,其工作是解析标签及其值并将其放入映射中。

Alo如何找到我正在解析的标签的父级,以便我可以跟踪孩子来自哪里,在这种情况下......公司0-&gt; staff-&gt; firstname。

3 个答案:

答案 0 :(得分:1)

可以通过以下更改来实现:

    for (int i=0; i<nodeList.getLength(); i++) 
    {
        // Get element
        Element element = (Element)nodeList.item(i);
        final NodeList nodes = element.getChildNodes();
        if(nodes.getLength() == 1)
        {               
            System.out.println(element.getNodeName() + " " + element.getTextContent());
        }            
    }

答案 1 :(得分:0)

JaxB将是一个更好的类,但你可以尝试这样简单的东西:

for (int i = 0; i < nl.getLength(); i++)
{
    node = nl.item(i);

    //check to see if node's name is what you don't want it to be
    if(node.getNodeName().equals("Staff") || node.getNodeName().equals("Comapny"))
    {
        //do stuff or dont do anything...
    }
    else//print other stuff
    {
        System.out.println("NodeName:[" + node.getNodeName() + "] Value:[" + node.getTextContent() + "]");
    }           
}

就您的第二个问题而言,我建议您查看Node API:

http://docs.oracle.com/javase/6/docs/api/org/w3c/dom/Node.html

提示:getParentNode()

如果您想要父级的最深层部分(名字,姓氏,工资),您可以获得第一个节点。然后执行node.getChildNodes()以获取子节点列表。彻底搜索每个孩子,直到你找到一个没有孩子的孩子。然后你知道这是一个叶子节点。你想打印它。

答案 2 :(得分:0)

您可以使用SAX解析器来解析XML并编写自己的处理程序来扩展DefaultHandler。

跟踪您在堆栈中读取的标记,并存储在调用字符()时读取的字符。调用endElement()时,从堆栈中弹出顶部标记,这是标记名称,而字符()读入的最后一个值是此标记的值。堆栈中剩下的字符串是导致此标记的父标记,例如

对于读取XML文件的主方法:

public static void main(String[] args) {
    File xmlFile = new File("somefile.xml");

    SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();

    MyHandler handler = new MyHandler();

    saxParser.parse(xmlFile, handler);

    Map<String, String> map = handler.getDataMap();
}

我们有自己的处理程序。

public class MyHandler extends DefaultHandler {
    private String characters = null;
    private Stack<String> tagStack;

    private Map<String, String> dataMap;

    public MyHandler() {
        this.tagStack = new Stack<String>();
        this.dataMap = new HashMap<String, String>();
    }   

    @Override
    public void startElement(String uri, String localName, String qName, 
             Attributes attributes) throws SAXException {
        this.tagStack.push(qName);
    }

    @Override
    public void characters(char[] ch, int start, int length) 
             throws SAXException {
        // trimming to take out whitespace between tags
        characters = new String(ch).trim();
    }

    @Override
    public void endElement(String uri, String localName,
            String qName) throws SAXException {
        // check that the end element we're looking at matches the last read 
        // startElement this should only happen if we don't have well-formed XML
        if (qName.equals(this.tagStack.peek())) {

            String[] tagArray = this.tagStack.toArray(new String[this.tagStack.size()]);

            // make use of apache-common-lang, or write your own code to concat 
            // the list with '.'s
            String tagHierarchy = StringUtils.join(tagArray, ".");
            this.dataMap.put(tagHierarchy, this.characters);

            // EDIT: I forgot to pop the last item off the stack :)
            this.tagStack.pop();
        } else {
            throw new SAXException("XML is not well-formed");
        }
    }

    public Map<String, String> getDataMap() {
        return this.dataMap;
    }

}

这将返回一个Map,其中使用OP中描述的输入数据:

["company.staff.firstname", "Kevin"]
["company.staff.lastname", "Gay"]
["company.staff.salary", "50000"]

如果您不想要元素的完整路径作为键,例如Map,其中key是标记名称,并且值[0]是父路径和值[1],则可以进行自己的调整。 ]是实际值等。