Как удалить дубликаты атрибутов из XML с C # - PullRequest
6 голосов
/ 07 июля 2011

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

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

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

Однако 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();
}

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

Ответы [ 2 ]

3 голосов
/ 13 июля 2011

Я нашел решение, думая о 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;
}

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

0 голосов
/ 07 июля 2011

Хорошо, думаю, вам нужно отловить ошибку:

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

reader.MoveToFirstAttribute();

и

reader.MoveToNextAttribute()

, чтобы получитьследующие свойства:

reader.Value
reader.Name

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

...