如何使用C#从XML中删除重复的属性

6
我正在解析来自第三方提供商的一些XML文件,不幸的是,有时一些元素包含重复属性,导致XML不是格式良好的。
我无法控制源,并且事先不知道哪些元素可能具有重复属性,也不知道重复属性名称。
显然,将内容加载到XMLDocument对象中会在重复属性上引发XmlException,因此我认为可以使用XmlReader逐个步进XML元素,并在到达有问题的元素时处理重复属性。
然而,在读取器读取(reader.Read())之前,XmlException就被引发了 - 我没有机会检查元素的属性。
以下是演示此问题的示例方法:
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处理器API提供任何挂钩以允许您挂钩进入处理并处理错误条件时,才可能实现。 - Ankur
有趣的问题,期待看到解决方案! - Kieren Johnstone
我认为你的问题已经在这里得到了答案:http://stackoverflow.com/questions/4085065/xml-linq-removing-duplicate-nodes-in-xelement-c - saj
2
使用XML无法解决此问题,因为您的输入不是XML。您说您无法控制输入,但您是否可以至少让您的上级意识到您的供应商没有向您发送XML?您是否可以确保您的供应商知道这一点?任何愚蠢到发送此数据的组织可能也愚蠢到没有意识到它不是XML。 - John Saunders
-1:不是的,那个问题是关于根据属性值删除重复元素的。而这个问题是关于“元素”具有多个相同属性的副本,比如<e a="1" a="2"/>,这不是XML。 - John Saunders
我想这是有道理的。所以我已经将内容视为字符串进行处理,在解析为XML之前对其进行清理。结果发现,供应商提供的XML文件中只有一个有问题的属性,而其他10,000行都是干净的。 - Catch22
2个回答

4
我通过将XML视为HTML文档来找到了解决方案。然后使用开源的Html Agility Pack库,我能够获取有效的XML。
诀窍是首先保存具有HTML头的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;
}

重复的属性节点会被自动移除,后面的属性值将覆盖前面的值。

非常感谢!这将为我们的支持团队节省大量手动编辑的时间。 - DylanSp

0

好的,我认为你需要捕获错误:

然后你应该能够使用以下方法:

reader.MoveToFirstAttribute();

并且

reader.MoveToNextAttribute()

获取以下属性:

reader.Value
reader.Name

这将使您能够获取所有属性值。


我可以捕获错误并处理当前节点上的属性(即复制非重复项),但问题在于继续处理文档的其余部分,因为reader.Read()返回false,所以不会处理更多元素。 - Catch22
#Catch22,是的,在尝试恢复代码时我遇到了这个问题。我希望你能找到解决方法。请看这里:http://bytes.com/topic/c-sharp/answers/827965-how-handle-xml-parsing-exception ,看起来XMLReader对错误很敏感是有原因的。这通常是好消息,但在你的情况下,这意味着我的建议可能行不通。抱歉。 - openshac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接