【问题标题】:namespaces and JDOM命名空间和 JDOM
【发布时间】:2014-03-13 19:00:42
【问题描述】:

我当前的代码是这样打印出 xml 的:

<type xmlns="http://www.example.com">
  <OBJECT_TYPE xmlns="">x3000</OBJECT_TYPE> 
- <prop xmlns="">
    <DESCRIPTION>a very fast train</DESCRIPTION> 
    <PARENT>NULL</PARENT> 
    <VIRTUAL>0</VIRTUAL> 
    <VISIBLE>1</VISIBLE> 
    <PICTURE>NULL</PICTURE> 
    <HELP>NULL</HELP> 
    <MIN_NO>NULL</MIN_NO> 
    <MAX_NO>NULL</MAX_NO> 
    <NAME_FORMAT>NULL</NAME_FORMAT> 
  </prop>
</type>

但我想要这个输出:

<type xmlns="http://www.example.com">
  <OBJECT_TYPE>x3000</OBJECT_TYPE> 
- <prop>
    <DESCRIPTION>a very fast train</DESCRIPTION> 
    <PARENT>NULL</PARENT> 
    <VIRTUAL>0</VIRTUAL> 
    <VISIBLE>1</VISIBLE> 
    <PICTURE>NULL</PICTURE> 
    <HELP>NULL</HELP> 
    <MIN_NO>NULL</MIN_NO> 
    <MAX_NO>NULL</MAX_NO> 
    <NAME_FORMAT>NULL</NAME_FORMAT> 
  </prop>
</type>

如何做到这一点?这是我当前的代码:

public void saveXmlToFile(Type objType, Properties property)
    throws IOException, ParserConfigurationException, SAXException,
    JDOMException {

        File xmlFile = new File(XMLEditorService.getXMLEditorService()
                .getFile());
        org.jdom2.Document doc = new SAXBuilder().build(xmlFile);
        Element root = doc.getRootElement();
        Namespace ns = Namespace.getNamespace("http://www.example.com");
        Element type = new Element("type");
        Element prop = new Element("prop");

        // Add <type> as a child of <root>
        root.addContent(type);

        // Set namespace on <type>
        type.setNamespace(ns);

        type.addContent(new Element("OBJECT_TYPE").setText(objType.getObjectType()));

        // Turn off namespace on <prop>
        prop.setNamespace(Namespace.NO_NAMESPACE);

        // Add <prop> as a child of <type>
        type.addContent(prop);

        prop.addContent(new Element("DESCRIPTION").setText(property.getDescription()));
        prop.addContent(new Element("PARENT").setText(property.getParent()));
        prop.addContent(new Element("VIRTUAL").setText(property.getVirtual()));
        prop.addContent(new Element("VISIBLE").setText(property.getVisible()));
        prop.addContent(new Element("PICTURE").setText(property.getPicture()));
        prop.addContent(new Element("HELP").setText(property.getHelp()));
        prop.addContent(new Element("MIN_NO").setText(property.getMin_no()));
        prop.addContent(new Element("MAX_NO").setText(property.getMax_no()));
        prop.addContent(new Element("NAME_FORMAT").setText(property.getName_format()));

        XMLOutputter xmlOutput = new XMLOutputter(Format.getPrettyFormat());
        // Create a new file and write XML to it
        xmlOutput.output(doc, new FileOutputStream(new File(XMLEditorService.getXMLEditorService().getFile())));
        System.out.println("Wrote to file");

}

【问题讨论】:

    标签: java xml namespaces jdom


    【解决方案1】:

    在 XML 文档中,如果将 xmlns="http://a.b.c" 添加到元素,则该元素及其所有后代元素将位于命名空间 "http://a.b.c" 中,除非后代元素碰巧更改了 xmlns 声明。这意味着,在 XML 文档中,默认命名空间“级联”到后代。这个过程很“方便”,因此 XML 不会那么“杂乱”。 “默认”命名空间是没有前缀的命名空间。将默认命名空间设置为某个值 "http://a.b.c" 的父元素的子元素“在”该命名空间中,即使它们的标语行上没有 xmlns="http://a.b.c" 声明。

    当 JDOM 对这样的文档进行建模时,它会将每个 Element 节点的命名空间设置为其应有的确切值。它不会“计算”或“级联”元素的命名空间。在 JDOM 中,如果您更改父元素的名称空间,它也不会更改子元素的名称空间。因此,例如,如果您有:

    <root xmlns="http://a.b.c">
        <child />
    </root>
    

    那么您在默认命名空间"http://a.b.c" 中同时拥有rootchild。该文档可以在 JDOM 中创建为:

    Namesapce ns = Namespace.get("http://a.b.c");
    Element root = new Element("root", ns);
    Element child = new Element("child", ns);
    root.addConent(child);
    

    请注意如何将 ns 添加到两个元素中。正如预期的那样,这个 JDOM 的输出将是:

    <root xmlns="http://a.b.c">
        <child />
    </root>
    

    现在,如果您更改根的命名空间:

    Namesapce ns = Namespace.get("http://a.b.c");
    Element root = new Element("root", ns);
    Element child = new Element("child", ns);
    root.addConent(child);
    root.setNamespace(Namespace.NO_NAMESPACE);
    

    你会得到:

    <root>
        <child xmlns="http://a.b.c"/>
    </root>
    

    但是,更有趣的是,如果你离开根命名空间,并改变子命名空间,

    Namesapce ns = Namespace.get("http://a.b.c");
    Element root = new Element("root", ns);
    Element child = new Element("child", ns);
    root.addConent(child);
    child.setNamespace(Namespace.NO_NAMESPACE);
    

    你得到:

    <root xmlns="http://a.b.c">
        <child xmlns="" />
    </root>
    

    当您创建一个没有命名空间参数 new Element("tag") 而不是 new Element("tag", Namespace) 的 JDOM 元素时,JDOM 将自动将新元素放入 NO_NAMESPACE 命名空间(与 new Element("tag", Namespace.NO_NAMESPACE); 相同)。这就是你正在做的。

    所以,JDOM 正在做您要求它做的事情......但是您要求它做的事情似乎并不是您想要的。

    你说你想要的是:

    <type xmlns="http://www.example.com">
      <OBJECT_TYPE>x3000</OBJECT_TYPE> 
    - <prop>
        <DESCRIPTION>a very fast train</DESCRIPTION> 
        <PARENT>NULL</PARENT> 
        <VIRTUAL>0</VIRTUAL> 
        <VISIBLE>1</VISIBLE> 
        <PICTURE>NULL</PICTURE> 
        <HELP>NULL</HELP> 
        <MIN_NO>NULL</MIN_NO> 
        <MAX_NO>NULL</MAX_NO> 
        <NAME_FORMAT>NULL</NAME_FORMAT> 
      </prop>
    </type>
    

    上面的 XML 在命名空间Namespace.getNamespace("http://www.example.com") 中有everything

    您的代码将很多东西放入 NO_NAMESPACE 命名空间。您的代码应该看起来像这样(注意我添加到new Element(...) 中的所有, ns)...

    请参阅 Element(String)Element(String, Namespace); 的 JavaDoc。

        org.jdom2.Document doc = new SAXBuilder().build(xmlFile);
        Element root = doc.getRootElement();
        Namespace ns = Namespace.getNamespace("http://www.example.com");
        Element type = new Element("type", ns);
        Element prop = new Element("prop", ns);
    
        // Add <type> as a child of <root>
        root.addContent(type);
    
        // Set namespace on <type>
        // type.setNamespace(ns); NO NEED, done on new Element("type", ns); above
    
        type.addContent(new Element("OBJECT_TYPE", ns).setText(objType.getObjectType()));
    
        // Turn off namespace on <prop>
        // NO!!!! You want to keep the namespace ON!!!
        // prop.setNamespace(Namespace.NO_NAMESPACE);
    
        // Add <prop> as a child of <type>
        type.addContent(prop);
    
        prop.addContent(new Element("DESCRIPTION", ns).setText(property.getDescription()));
        prop.addContent(new Element("PARENT", ns).setText(property.getParent()));
        prop.addContent(new Element("VIRTUAL", ns).setText(property.getVirtual()));
        prop.addContent(new Element("VISIBLE", ns).setText(property.getVisible()));
        prop.addContent(new Element("PICTURE", ns).setText(property.getPicture()));
        prop.addContent(new Element("HELP", ns).setText(property.getHelp()));
        prop.addContent(new Element("MIN_NO", ns).setText(property.getMin_no()));
        prop.addContent(new Element("MAX_NO", ns).setText(property.getMax_no()));
        prop.addContent(new Element("NAME_FORMAT", ns).setText(property.getName_format()));
    
        XMLOutputter xmlOutput = new XMLOutputter(Format.getPrettyFormat());
        // Create a new file and write XML to it
        xmlOutput.output(doc, new FileOutputStream(new File(XMLEditorService.getXMLEditorService().getFile())));
        System.out.println("Wrote to file");
    

    在上面的代码中,everything都在"http://www.example.com"命名空间中,因此JDOM只需要输出带有xmlns声明的顶级元素,你应该得到你想要的。

    对于它的价值,你不能只将这种混乱归咎于 JDOM.... 命名空间的本质是复杂的。

    【讨论】:

    • 您没有像我在代码中那样将, ns 添加到所有prop.addContent(....) 中,是吗?
    • 是的。这与我所做的相反。这种解释值得大量赞成。我在 Stackoverflow 上读过的最好的书之一 :-)
    • @Sembrano 帮助我写了一大段代码......几天前当我要求提供更多信息时,你应该帮助我;-) 而不是删除你的问题,你本可以为自己节省大量压力。
    • 如果我想做隐藏命名空间之类的反对 如果我只想打印 那我该怎么做?
    • 删除所有, ns成员(全部),然后所有内容都将位于默认命名空间中,即“”....或更改Namespace ns = Namespace.NO_NAMESPACE;
    猜你喜欢
    • 2011-03-11
    • 2010-10-07
    • 1970-01-01
    • 2012-05-22
    • 2012-04-02
    • 2010-10-20
    • 1970-01-01
    • 2011-02-06
    • 1970-01-01
    相关资源
    最近更新 更多