【问题标题】:XML child node attribute valueXML子节点属性值
【发布时间】:2012-11-22 00:36:34
【问题描述】:

我正在尝试读取 xml 文件,例如:

<entry>
    <title>FEED TITLE</title>
    <id>5467sdad98787ad3149878sasda</id>
    <tempi type="application/xml">
      <conento xmlns="http://mydomainname.com/xsd/radiofeed.xsd" madeIn="USA" />
    </tempi>
</entry>

这是我到目前为止的代码:

这是我尝试编写代码的尝试,说什么不成功,这就是我开始赏金的原因。这是http://pastebin.com/huKP4KED

赏金更新:

我真的真的很想这样做好几天了,没想到会这么难,我会接受有用的链接/书籍/教程,但更喜欢代码,因为我需要在昨天完成。

这是我需要的:

关于上面的xml:

  • 我需要获取 title 的值,id
  • tempi的属性值以及contento的madeIn属性值

最好的方法是什么?

编辑:

@Pascal Thivent

也许创建方法是个好主意,比如 public String getValue(String xml, Element elementname),你指定标签名称,方法返回标签值或标签属性(也许给它名称作为附加的方法参数)如果值是不可用

如果标签值不可用,我真正想获得某些标签值或属性,所以我正在考虑最好的方法是什么,因为我以前从未这样做过

【问题讨论】:

    标签: java xml


    【解决方案1】:

    最好的解决方案是使用 XPath。您的 pastebin 已过期,但这是我收集的。假设我们有以下feed.xml 文件:

    <?xml version="1.0" encoding="UTF-8" ?>
    <entries>
    <entry>
        <title>FEED TITLE 1</title>
        <id>id1</id>
        <tempi type="type1">
          <conento xmlns="dontcare?" madeIn="MadeIn1" />
        </tempi>
    </entry>
    <entry>
        <title>FEED TITLE 2</title>
        <id>id2</id>
        <tempi type="type2">
          <conento xmlns="dontcare?" madeIn="MadeIn2" />
        </tempi>
    </entry>
    <entry>
        <id>id3</id>
    </entry>
    </entries>
    

    这是一个简短但可编译和运行的概念证明(feed.xml 文件位于同一目录中)。

    import javax.xml.xpath.*;
    import javax.xml.parsers.*;
    import org.w3c.dom.*;
    import java.io.*;
    import java.util.*;
    
    public class XPathTest {
        static class Entry {
            final String title, id, origin, type;
            Entry(String title, String id, String origin, String type) {
                this.title = title;
                this.id = id;
                this.origin = origin;
                this.type = type;
            }
            @Override public String toString() {
                return String.format("%s:%s(%s)[%s]", id, title, origin, type);
            }
        }
    
        final static XPath xpath = XPathFactory.newInstance().newXPath();
        static String evalString(Node context, String path) throws XPathExpressionException {
            return (String) xpath.evaluate(path, context, XPathConstants.STRING);
        }
    
        public static void main(String[] args) throws Exception {
            File file = new File("feed.xml");
            Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
            NodeList entriesNodeList = (NodeList) xpath.evaluate("//entry", document, XPathConstants.NODESET);
    
            List<Entry> entries = new ArrayList<Entry>();
            for (int i = 0; i < entriesNodeList.getLength(); i++) {
                Node entryNode = entriesNodeList.item(i);
                entries.add(new Entry(
                    evalString(entryNode, "title"),
                    evalString(entryNode, "id"),
                    evalString(entryNode, "tempi/conento/@madeIn"),
                    evalString(entryNode, "tempi/@type")
                ));
            }
            for (Entry entry : entries) {
                System.out.println(entry);
            }
        }
    }
    

    这会产生以下输出:

    id1:FEED TITLE 1(MadeIn1)[type1]
    id2:FEED TITLE 2(MadeIn2)[type2]
    id3:()[]
    

    请注意使用 XPath 如何使值检索变得非常简单、直观、可读和直接,并且还可以优雅地处理“缺失”值。

    API 链接

    【讨论】:

    • @polygenelubricants 我在这里看到了这种方法java.sun.com/developer/technicalArticles/xml/mapping,但不确定如何实现它。所以建议我创建带有字段 title、id、tempi 的 Entry 类?我没有关注你我将如何使用它而不是 map 以及如何使用这种方法获取标签属性?
    • @c0mrade:问题是如何表示数据,而不是如何_get _them。您仍然可以使用 XPath 获取值,如上所示,但不是将它们存储到 Map&lt;String,String&gt; 中,而是要使用具有 getId()getTitle()getCountryOfOrigin()class Entry,等等。这是您自己的class Entry,因此您可以定义应该调用哪些字段以及它们的含义。
    • @c0mrade:我已将示例修改为使用class Entry 而不是Map&lt;String,String&gt;。不过,重点是 XPath 的使用。
    • @polygenelubricants 非常感谢,这就是我一直在追求的,我在 printFeed() 中创建了单独的 Entry 类并创建了方法,并且刚刚粘贴了你的 toString 方法的返回。太好了,谢谢,我应该等待更多时间还是现在接受赏金?
    • @c0mrade:何时接受完全取决于您。现在、以后或(吞!)永远不要接受是你的权利。
    【解决方案2】:

    使用Element.getAttribute 和 Element.setAttribute

    在您的示例中,((Node) content.item(0)).getFirstChild().getAttributes()。假设content 是一个错字,你的意思是contento,getFirstChild 正确返回NULL,因为contento 没有孩子。试试:((Node) contento.item(0)).getAttributes()

    另一个问题是,如果在不检查返回值的情况下使用 getFirstChildgetChildNodes()[0],您将面临选择子文本节点而不是您想要的元素的风险。

    【讨论】:

    • 谢谢你的回答,((Node) contento.item(0)).getAttributes()返回null,
    • 在这种情况下,您几乎肯定会选择子文本节点。尝试((Node) contento.item(1)).getAttributes(),如果成功,则重构它以使您的意图更清晰(可能使用getNodeType)。
    【解决方案3】:

    正如所指出的,&lt;contento&gt; 没有任何孩子所以而不是:

    (contento.item(0)).getFirstChild().getAttributes()
    

    您应该将节点视为元素并使用 getAttribute(String),如下所示:

    ((Element)contento.item(0)).getAttribute("madeIn")
    

    这是您的代码的修改版本(它不是我编写的最强大的代码):

    InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(inputStream);
    doc.getDocumentElement().normalize();
    System.out.println("Root element " + doc.getDocumentElement().getNodeName());
    NodeList nodeLst = doc.getElementsByTagName("entry");
    System.out.println("Information of all entries");
    
    for (int s = 0; s < nodeLst.getLength(); s++) {
    
        Node fstNode = nodeLst.item(s);
    
        if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
    
            Element fstElmnt = (Element) fstNode;
    
            NodeList title = fstElmnt.getElementsByTagName("title").item(0).getChildNodes();
            System.out.println("Title : " + (title.item(0)).getNodeValue());
    
            NodeList id = fstElmnt.getElementsByTagName("id").item(0).getChildNodes();
            System.out.println("Id: " + (id.item(0)).getNodeValue());
    
            Node tempiNode = fstElmnt.getElementsByTagName("tempi").item(0);
            System.out.println("Type : " + ((Element) tempiNode).getAttribute("type"));
    
            Node contento = tempiNode.getChildNodes().item(0);
            System.out.println("Made in : " + ((Element) contento).getAttribute("madeIn"));
        }
    }
    

    在您的 XML sn-p 上运行它会产生以下输出:

    根元素条目 所有条目的信息 标题:饲料标题 编号:5467sdad98787ad3149878sasda 类型:应用程序/xml 美国制造

    顺便问一下,您是否考虑过改用 Rome 之类的东西?

    【讨论】:

    • @Pascal Thivent 我确实看过罗马,在我的情况下,我从其他方法获得了 xml 作为字符串。与罗马一起这样做会更好吗?有可能创造出比这更通用的东西吗?我的意思是这通常是这样做的吗? +1
    • 你为什么要getAttributes().item(0)?如果 xml 元素曾经交换过其属性的位置,这将导致错误的属性。 node.getAttribute('madeIn') 似乎工作正常...
    • @seanmonstar 它确实工作正常,是的,这就是我所说的“更通用”的意思
    • @c0mrade:String 没问题,罗马可以拿一个InputStream。我至少想提一下,因为我认为 Rome 是解析/聚合/生成提要的事实上的标准,它提供了一个漂亮而干净的 API。根据您想要做什么,罗马可能确实是标准选择。
    • 另外,请记住,属性的顺序在 XML 中并不重要——处理器可以按任何顺序有效地返回属性列表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多