пространства имен и JDOM

Мой текущий код распечатывает 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>

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

}

person Sembrano    schedule 14.02.2014    source источник


Ответы (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>

Тогда у вас есть и root, и child в пространстве имен по умолчанию "http://a.b.c". Этот документ может быть создан в 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>

Когда вы создаете элемент JDOM без аргумента пространства имен new Element("tag") вместо new Element("tag", Namespace), 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").

Вместо этого ваш код помещает много вещей в пространство имен NO_NAMESPACE. Ваш код, вероятно, должен выглядеть так (обратите внимание на все , ns, которые я добавил к new Element(...))...

См. JavaDoc для Element(String) и для Элемент(Строка, Пространство имен);.

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

В приведенном выше коде все находится в пространстве имен "http://www.example.com", и в результате JDOM нужно вывести только элемент верхнего уровня с объявлением xmlns, и вы должны получить именно то, что хотите.

Как бы то ни было, вы не можете винить только JDOM в этой путанице... природа пространств имен состоит в том, что они сложны.

person rolfl    schedule 14.02.2014
comment
Вы же не добавили , ns ко всем prop.addContent(....), как у меня в коде, не так ли? - person rolfl; 14.02.2014
comment
да правильно. Это сработало противоположно тому, что я сделал. Это объяснение заслуживает большого количества голосов. один из лучших, которые я когда-либо читал на Stackoverflow :-) - person Sembrano; 14.02.2014
comment
@Sembrano помогает то, что я написал кусок этого кода .... вы должны были помочь мне несколько дней назад, когда я попросил дополнительную информацию ;-) Вместо того, чтобы удалять свой вопрос, вы могли бы избавить себя от тонны стресса. - person rolfl; 14.02.2014
comment
Если я хочу сделать обратное, например, скрыть пространства имен. Если я хочу напечатать просто ‹type›, что мне тогда делать? - person Sembrano; 14.02.2014
comment
Удалите всех членов , ns (всех), и тогда все будет в пространстве имен по умолчанию, которое будет .... или измените Namespace ns = Namespace.NO_NAMESPACE; - person rolfl; 14.02.2014
comment
хм, это просто напечатает ‹type xmlns=› не ‹type› - person Sembrano; 14.02.2014
comment
Затем тип находится внутри какого-то другого элемента, который имеет другое пространство имен. и вы не показываете мне, что это за другой элемент (или каково его пространство имен) - person rolfl; 14.02.2014
comment
да, у корня есть пространство имен, так что я плохо угадываю, просто делаю: root.setNamespace(Namespace.NO_NAMESPACE); - person Sembrano; 14.02.2014