문제

My current code is printing out the xml like this :

<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>

But I want this output :

<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>

How to do that ? This is my current code :

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");

}
도움이 되었습니까?

해결책

In an XML document, if you add xmlns="http://a.b.c" to an Element, then that Element, and all it's descendant Elements, will be in the namespace "http://a.b.c", unless the descendant Elements happen to change the xmlns declaration. This means that, in an XML Document, the default namespace 'cascades' to the descendants. This process is a 'convenience' so that the XML is not as 'cluttered'. The 'default' namespace is the namespace that has no prefix. The Child Elements of a parent that sets the default Namespace to some value "http://a.b.c" are 'in' that namespace, even though they do not have an xmlns="http://a.b.c" declaration on their tag-line.

When JDOM models a document like this, it sets the Namespace of each Element node to the exact valu it should be. It does not 'calculate', or 'cascade' the namespace of an Element. In JDOM, if you change the namespace of a parent Element, it does not change the namespaces of the child elements as well. So, for example, if you have:

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

Then you have both root and child in the default Namespace "http://a.b.c". That document could be created, in JDOM, as:

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

Note how ns was added to both Elements. The output of this JDOM would be, as expeced:

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

Now, if you change the root's Namespace:

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);

you will get:

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

But, more interesting, if you leave the root namespace, and change the child namespace,

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);

you get:

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

When you create a JDOM Element without a namespace argument new Element("tag") instead of new Element("tag", Namespace) then JDOM will automatically put the new Element in the NO_NAMESPACE namespace (the same as new Element("tag", Namespace.NO_NAMESPACE); ). This is what you are doing.

So, JDOM is doing what you ask it to do... but what you are asking it to do does not appear to be what you want.

What you say you want is:

<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>

The above XML has everything in the namespace Namespace.getNamespace("http://www.example.com").

Your code is putting a lot of things in to the NO_NAMESPACE namespace instead. Your code should probably look like this (note all the , ns I have added to the new Element(...))...

See the JavaDoc for Element(String), and for Element(String, Namespace);.

    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");

In the above code, everything is in the "http://www.example.com" namespace, and, as a result, JDOM only needs to output the top-level Element with an xmlns declaration, and you should get exactly what you want.

For what it's worth, you can't only blame JDOM for this confusion.... it is the nature of Namespaces that it is complicated.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top