Как удалить повторяющиеся атрибуты из XML с помощью C #

c# html-agility-pack validation xml

Вопрос

Я разбираю некоторые XML-файлы от стороннего поставщика и, к сожалению, это не всегда хорошо сформированный XML, поскольку иногда некоторые элементы содержат повторяющиеся атрибуты.

У меня нет контроля над исходным кодом, и я не знаю, какие элементы могут иметь повторяющиеся атрибуты, и я не знаю имена дубликатов атрибутов заранее.

Очевидно, что загрузка содержимого в объект XMLDocument вызывает XMLDocument XmlException для повторяющихся атрибутов, поэтому, хотя я мог бы использовать XmlReader для шага, хотя элемент XML по элементу, и иметь дело с дублирующимися атрибутами, когда я попадаю в оскорбительный элемент.

Тем не менее, reader.Read() XmlException возникает на reader.Read() - до того, как я получу шанс обработать атрибуты элемента.

Вот пример метода для демонстрации проблемы:

public static void ParseTest()
{
    const string xmlString = 
        @"<?xml version='1.0'?>
        <!-- This is a sample XML document -->
        <Items dupattr=""10"" id=""20"" dupattr=""33"">
            <Item>test with a child element <more/> stuff</Item>
        </Items>";

    var output = new StringBuilder();
    using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
    {
        XmlWriterSettings ws = new XmlWriterSettings();
        ws.Indent = true;
        using (XmlWriter writer = XmlWriter.Create(output, ws))
        {
            while (reader.Read())   /* Exception throw here when Items element encountered */
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        writer.WriteStartElement(reader.Name);
                        if (reader.HasAttributes){ /* CopyNonDuplicateAttributes(); */}
                        break;
                    case XmlNodeType.Text:
                        writer.WriteString(reader.Value);
                        break;
                    case XmlNodeType.XmlDeclaration:
                    case XmlNodeType.ProcessingInstruction:
                        writer.WriteProcessingInstruction(reader.Name, reader.Value);
                        break;
                    case XmlNodeType.Comment:
                        writer.WriteComment(reader.Value);
                        break;
                    case XmlNodeType.EndElement:
                        writer.WriteFullEndElement();
                        break;
                }
            }

        }
    }
    string str = output.ToString();
}

Есть ли другой способ проанализировать ввод и удалить повторяющиеся атрибуты без использования регулярных выражений и строковых манипуляций?

Принятый ответ

Я нашел решение, рассматривая XML как HTML-документ. Затем, используя библиотеку Html Agility Pack с открытым исходным кодом, я смог получить действительный XML.

Трюк состоял в том, чтобы сначала сохранить xml с заголовком HTML.
Поэтому замените декларацию XML
<?xml version="1.0" encoding="utf-8" ?>
с объявлением HTML следующим образом:
!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

После сохранения содержимого в файл этот метод вернет действительный XML-документ.

// Requires reference to HtmlAgilityPack
public XmlDocument LoadHtmlAsXml(string url)
{
    var web = new HtmlWeb();

    var m = new MemoryStream();
    var xtw = new XmlTextWriter(m, null);

    // Load the content into the writer
    web.LoadHtmlAsXml(url, xtw);

    // Rewind the memory stream
    m.Position = 0;

    // Create, fill, and return the xml document
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml((new StreamReader(m)).ReadToEnd());
    return xmlDoc;
}

Дублированные узлы атрибутов автоматически удаляются с более поздними значениями атрибутов, перезаписывающими более ранние.


Популярные ответы

Хорошо, что вам нужно поймать ошибку:

Затем вы сможете использовать следующие методы:

reader.MoveToFirstAttribute();

а также

reader.MoveToNextAttribute()

для получения следующих свойств:

reader.Value
reader.Name

Это позволит вам получить все значения атрибутов.



Related

Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow