none
Как в .NET извлечь из XML-документа кусок "как есть" (без добавления пространств имен) с использованием XPath? RRS feed

  • Вопрос

  • Пример:

    <?xml version="1.0"  encoding="utf-8" ?>
    <BigDocument xmlns="http://example.com/BigDocument" xmlns:add="http://example.com/AdditionalNamespace">
    <SomeTag>
    <add:AnotherTag>foo</add:AnotherTag>
    </SomeTag>
    <Content>
    <inn:InnerDoc xmlns:inn="http://example.com/InnerDoc">
    <inn:FirstTag>boo</inn:FirstTag>
    <add:SecondTag>1</add:SecondTag>
    </inn:InnerDoc>
    </Content>
    </BigDocument>

    Требуется получить:

    <inn:InnerDoc xmlns:inn="http://example.com/InnerDoc">
    <inn:FirstTag>boo</inn:FirstTag>
    <add:SecondTag>1</add:SecondTag>
    </inn:InnerDoc>

    Но с использованием XPathNavigator (XmlDocument.CreateNavigator) при выборе получается:

    <inn:InnerDoc xmlns:inn="http://example.com/InnerDoc">
    <inn:FirstTag>boo</inn:FirstTag>
    <add:SecondTag xmlns:add="http://example.com/AdditionalNamespace">1</add:SecondTag>
    </inn:InnerDoc>
    Как в итоге извлечь содержимое без изменения?



    • Изменено Gregory.K 19 октября 2012 г. 9:16
    19 октября 2012 г. 9:14

Ответы

  • Дело в том, что каждому префиксу соответствует некое пространство имён. У элемента SecondTag есть префикс add, ему обязательно должен быть сопоставлен нэймспейс.

    Если как-то искусственно создать кусок xml без указания всех необходимых namespace и подать его на вход любого xml-процессора, то xml-процессор обязан выдать ошибку в соответствии со спецификацией.

    Непосредственно из SecondTag'а можно убрать указание нэймспейса, если перенести его выше. Например, так:

    XmlDocument doc = new XmlDocument();
    doc.Load("test.xml");
    XPathNavigator nav = doc.CreateNavigator();
    XmlNamespaceManager man = new XmlNamespaceManager(nav.NameTable);
    man.AddNamespace("x", "http://example.com/BigDocument");
    man.AddNamespace("inn", "http://example.com/InnerDoc");
    
    var innerDoc = nav.SelectSingleNode("//inn:InnerDoc", man);
    Console.WriteLine(innerDoc.OuterXml);
    Console.WriteLine();
    
    var content = nav.SelectSingleNode("//x:Content", man);
    content.CreateAttribute("xmlns", "add", "", "http://example.com/AdditionalNamespace");
    Console.WriteLine(content.OuterXml);
    Console.WriteLine();

    Здесь в узле innerDoc нэймспейс будет указан в элементе SecondTag. А в узле Content я добавил атрибут с нашим нэймспейсом, и, соответственно, из элемента SecondTag исчезло определение namespace.

    • Предложено в качестве ответа Abolmasov Dmitry 22 октября 2012 г. 7:34
    • Помечено в качестве ответа Abolmasov Dmitry 23 октября 2012 г. 7:52
    19 октября 2012 г. 11:14
  • Привет.

    А что дальше должно происходить с кусками xml, так ли критичны данные пространства имен?

    По вашему вопросу ответ - скорее всего никак, т.к. раз вы пользуетесь классами для работы с xml, то они работают только с валидными xml и должны соблюдать правила как для чтения, так и для генерации xml и элементов xml.


    Для связи [mail]


    • Изменено Abolmasov Dmitry 22 октября 2012 г. 7:49
    • Помечено в качестве ответа Gregory.K 23 октября 2012 г. 4:54
    22 октября 2012 г. 7:35

Все ответы

  • Дело в том, что каждому префиксу соответствует некое пространство имён. У элемента SecondTag есть префикс add, ему обязательно должен быть сопоставлен нэймспейс.

    Если как-то искусственно создать кусок xml без указания всех необходимых namespace и подать его на вход любого xml-процессора, то xml-процессор обязан выдать ошибку в соответствии со спецификацией.

    Непосредственно из SecondTag'а можно убрать указание нэймспейса, если перенести его выше. Например, так:

    XmlDocument doc = new XmlDocument();
    doc.Load("test.xml");
    XPathNavigator nav = doc.CreateNavigator();
    XmlNamespaceManager man = new XmlNamespaceManager(nav.NameTable);
    man.AddNamespace("x", "http://example.com/BigDocument");
    man.AddNamespace("inn", "http://example.com/InnerDoc");
    
    var innerDoc = nav.SelectSingleNode("//inn:InnerDoc", man);
    Console.WriteLine(innerDoc.OuterXml);
    Console.WriteLine();
    
    var content = nav.SelectSingleNode("//x:Content", man);
    content.CreateAttribute("xmlns", "add", "", "http://example.com/AdditionalNamespace");
    Console.WriteLine(content.OuterXml);
    Console.WriteLine();

    Здесь в узле innerDoc нэймспейс будет указан в элементе SecondTag. А в узле Content я добавил атрибут с нашим нэймспейсом, и, соответственно, из элемента SecondTag исчезло определение namespace.

    • Предложено в качестве ответа Abolmasov Dmitry 22 октября 2012 г. 7:34
    • Помечено в качестве ответа Abolmasov Dmitry 23 октября 2012 г. 7:52
    19 октября 2012 г. 11:14
  • Спасибо за оперативный ответ.

    Имеющаяся задача стоит таким образом, что нужно выдергивать куски по произвольным XPath'ам в произвольных XML-документах: заранее неизвестно, какие префиксы используются во вложенном куске и какие из соответствующих пространств имен определены в выдергиваемом куске, поэтому искусственно добавить определения для пространств имен (на уровень выше) не получится, а даже если и добавить, то InnerXml вернет кусок с объявлением недостающих ns (т.к. он слишком умный и понимает, что этот кусок будет невалидным без ns).



    • Изменено Gregory.K 19 октября 2012 г. 12:48
    19 октября 2012 г. 11:55
  •  InnerXml вернет кусок с объявлением недостающих ns (т.к. он слишком умный и понимает, что этот кусок будет невалидным без ns).

    Вот и хорошо! Xml будет валидным. Вообще непонятно, почему вас беспокоит наличие указаний ns. Пусть будут :). Если потом эти куски сливать в один большой xml, то лишние объявления ns уберутся сами собой, при наличие таких же уровнем выше.
    19 октября 2012 г. 15:31
  • Привет.

    А что дальше должно происходить с кусками xml, так ли критичны данные пространства имен?

    По вашему вопросу ответ - скорее всего никак, т.к. раз вы пользуетесь классами для работы с xml, то они работают только с валидными xml и должны соблюдать правила как для чтения, так и для генерации xml и элементов xml.


    Для связи [mail]


    • Изменено Abolmasov Dmitry 22 октября 2012 г. 7:49
    • Помечено в качестве ответа Gregory.K 23 октября 2012 г. 4:54
    22 октября 2012 г. 7:35
  • Приветствую.

    Неизменность куска XML - это внешнее требование. Дальше с ним уже будут делать все, что угодно: валидировать, проверять или накладывать ЭЦП и еще Бог весть что.

    Раз встроенный "движок" XML не дает никакой свободы действий, придется писать собственную XPath-дергалку "XML-мяса".

    Спасибо за подсказки.

    23 октября 2012 г. 4:52