Десериализовать XML-фрагмент с пространством имен с помощью С#

У меня возникают проблемы с десериализацией следующего фрагмента XML (из OneNote):

<one:OE creationTime="2015-03-21T18:32:38.000Z" lastModifiedTime="2015-03-21T18:32:38.000Z" objectID="{649CA68C-C596-4F89-9885-1553A953529E}{30}{B0}" alignment="left" quickStyleIndex="1" selected="partial">
    <one:List>
        <one:Bullet bullet="2" fontSize="11.0" />
    </one:List>
    <one:T><![CDATA[Bullet point one]]></one:T>
</one:OE>

Следующий код используется для десериализации приведенного выше фрагмента. Класс OE имеет следующие атрибуты:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34230")]
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://schemas.microsoft.com/office/onenote/2013/onenote")]
[System.Xml.Serialization.XmlRootAttribute("OE", Namespace = "http://schemas.microsoft.com/office/onenote/2013/onenote", IsNullable = true)]
public partial class OE : EntityBase<OE>
{
    ...
}

И фактический метод десериализации фрагмента находится в базовом классе EntityBase:

public static T Deserialize(string xml)
{
    System.IO.StringReader stringReader = null;
    try
    {
        stringReader = new System.IO.StringReader(xml);
        return ((T)(Serializer.Deserialize(System.Xml.XmlReader.Create(stringReader))));
    }
    finally
    {
        if ((stringReader != null))
        {
            stringReader.Dispose();
        }
    }
}

Метод десериализации вызывается следующим образом:

var element = OE.Deserialize(xmlString);

Где переменная xmlString — это приведенный выше XML-фрагмент. При вызове метода Deserialize я получаю следующую ошибку:

There is an error in XML document (1,2). ---> System.Xml.XmlException: 'one' is an undeclared prefix. Line 1, position 2.

Я потратил некоторое время на просмотр атрибутов, объявляющих пространства имен в классе OE, но все выглядит правильно. Может ли кто-нибудь указать на ошибку, которую я делаю?


person MagicAndi    schedule 21.04.2015    source источник
comment
Ошибка сообщает вам точную проблему: пространство имен one не определено. т.е. не существует атрибута xmlns, который инициирует one.   -  person Der Kommissar    schedule 21.04.2015
comment
Поскольку объявление пространства имен отсутствует, я предполагаю, что оно просто вырезано из большего XML-файла? Делается программно? Если да, покажите этот код — его нужно изменить, чтобы он поддерживал XML. Отсечение этого элемента грубой силой приводит к недопустимому XML.   -  person Charles Mager    schedule 21.04.2015


Ответы (2)


Я думаю, вам нужно исходное объявление пространства имен для one. Это связано с тем, что one является пространством имен, а такие элементы, как OE и List, являются префиксами, которые существуют в пространстве имен, созданном oneNote, объявление которого отсутствует в опубликованном вами фрагменте. Префикс существует, чтобы избежать коллизий в именовании в случае смешивания разных XML-документов. дополнительные пояснения см. по ссылке w3schools

Таким образом, обходным путем было бы добавить пространство имен, такое как <xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote">, к каждому фрагменту (кажется, не самым оптимальным, но это работает) и продолжить его десериализацию, как вы это сделали.

У меня нет под рукой OneNote, поэтому объявление пространства имен было из этого сообщения на форуме.

Альтернативный способ десериализации XML-фрагментов — через XMLReader и XMLReaderSettings, где можно установить уровень соответствия Fragment. И добавление предопределенного пространства имен.

Пример адаптирован из этот блог MSDN

 XmlDocument doc = new XmlDocument();

        NameTable nt = new NameTable();
        XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
        nsmgr.AddNamespace("one", "http://schemas.microsoft.com/office/onenote/2010/onenote");
        XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
        XmlReaderSettings xset = new XmlReaderSettings();
        xset.ConformanceLevel = ConformanceLevel.Fragment;
        XmlReader rd = XmlReader.Create(new  StringReader(XMLSource), xset, context); 

        doc.Load(rd);

Я лично предпочитаю использовать настройки XMLReader и XMLReader, хотя они кажутся более трудоемкими, чтобы CreateReader() и настроить все, это выглядит более надежным способом.

Или, если вы не хотите иметь дело с пользовательскими пространствами имен и тому подобным, и не сталкиваетесь с проблемой коллизий, просто программно удалите переднее объявление one: и продолжите его десериализацию. . Однако это больше касается манипуляций со строками.

person matrixanomaly    schedule 21.04.2015
comment
Не по теме, но если вы хотите установить пространство имен по умолчанию для фрагмента, просто добавьте его URI в диспетчер пространства имен с префиксом String.Empty. - person GHC; 09.02.2018

ответ, данный matrixanomaly верно, но, к сожалению, указанное пространство имен OneNote неверно. Я работаю с OneNote 2013, а не с 2010. Фактический код, который я использовал для десериализации того же фрагмента XML, что и в моем вопросе, выглядит следующим образом:

public static OE DeserializeFragment(string xmlFragment)
{
    var serializer = new System.Xml.Serialization.XmlSerializer(typeof(OE));
    System.IO.StringReader stringReader = null;
    try
    {
        stringReader = new System.IO.StringReader(xmlFragment);

        NameTable nt = new NameTable();
        XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
        nsManager.AddNamespace("one", "http://schemas.microsoft.com/office/onenote/2013/onenote");
        XmlParserContext context = new XmlParserContext(null, nsManager, null, XmlSpace.None);
        XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
        xmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment;

        return ((OE)(serializer.Deserialize(System.Xml.XmlReader.Create(stringReader, xmlReaderSettings, context))));
    }
    finally
    {
        if ((stringReader != null))
        {
            stringReader.Dispose();
        }
    }
}
person MagicAndi    schedule 23.04.2015
comment
странно, меня не уведомили о вашем упоминании, я увидел это только тогда, когда пересматривал некоторые из своих ответов! +1 за обновление! - person matrixanomaly; 27.04.2015