删除XML中的重复元素

时间:2013-09-30 11:02:08

标签: c# xml

我的项目需要将输入XML文件转换为DataTable的功能。 我使用以下代码来做到这一点。

 DataSet ds = new DataSet();
 ds.Locale = CultureInfo.InvariantCulture;
 dataSourceFileStream.Seek(0, SeekOrigin.Begin);
 ds.ReadXml(dataSourceFileStream);
 dt = ds.Tables[0];

除非输入XML具有重复元素,否则它会正常工作,例如,如果XML文件如下所示:

<?xml version="1.0" encoding="iso-8859-1"?>
<DocumentElement>
 <data>
   <DATE>27 September 2013</DATE>
   <SCHEME>Test Scheme Name</SCHEME>
   <NAME>Mr John</NAME>
   <SCHEME>Test Scheme Name</SCHEME>
  <TYPE>1</TYPE>
 </data>
</DocumentElement>

如上所示,元素 SCHEME 出现两次。当这种XML文件到来时ds.ReadXml(dataSourceFileStream);无法返回正确的数据表。

有更好的方法来解决这个问题吗?

2 个答案:

答案 0 :(得分:1)

看起来你必须先修复XML。您可以使用XDocument和关联的类来完成此操作。但首先需要创建一个EqualityComparer,它根据名称比较两个XElements:

    public class MyEqualityComparer : IEqualityComparer<XElement>
    {
        public bool Equals(XElement x, XElement y)
        {
            return x.Name == y.Name;
        }

        public int GetHashCode(XElement obj)
        {
            return obj.Name.GetHashCode();
        }
    }

现在试试这个:

        var comparer = new MyEqualityComparer();

        XDocument.Load(dataSourceFileStream);

        var doc = XDocument.Parse(data);

        var dataElements = doc.Element("DocumentElement").Elements("data");
        foreach (var dataElement in dataElements)
        {
            var childElements = dataElement.Elements();
            var distinctElements = childElements.Distinct(comparer).ToArray();
            if (distinctElements.Length != childElements.Count())
            {
                dataElement.Elements().Remove();
                foreach (var item in distinctElements)
                    dataElement.Add(item);
            }
        }

        using (var stream = new MemoryStream())
        {
            var writer = new StreamWriter(stream);
            doc.Save(writer);

            stream.Seek(0, 0);

            var ds = new DataSet();
            ds.Locale = CultureInfo.InvariantCulture;
            var mode = ds.ReadXml(stream);
            var dt = ds.Tables[0];      
        }

这将是您的问题的快速解决方法。但我强烈建议鼓励数据提供者修复XML

答案 1 :(得分:1)

好。正如我之前的评论中所述,您可以创建自己的XmlTextReader来修补/忽略某些元素。这个想法是,这个读者检查他是否已经读取了相同深度的元素。如果是这种情况,请前进到结束元素。

    class MyXmlReaderPatcher : XmlTextReader
    {
        private readonly HashSet<string> _currentNodeElementNames = new HashSet<string>();

        public MyXmlReaderPatcher(TextReader reader) : base(reader)
        { }

        public override bool Read()
        {
            var result = base.Read();

            if (this.Depth == 1)
            {
                _currentNodeElementNames.Clear();
            }
            else if (this.Depth==2 && this.NodeType == XmlNodeType.Element)
            {
                if (_currentNodeElementNames.Contains(this.Name))
                {
                    var name = this.Name;

                    do {
                        result = base.Read();
                        if (result == false)
                            return false;
                    } while (this.NodeType != XmlNodeType.EndElement && this.Name != name);

                    result = this.Read();
                }
                else
                {
                    _currentNodeElementNames.Add(this.Name);
                }
            }

            return result;
        }
    }

您所要做的就是在ds.ReadXml()和文件流之间链接新的阅读器:

        var myReader = new MyXmlReaderPatcher(dataSourceFileStream);

        var ds = new DataSet();
        ds.Locale = CultureInfo.InvariantCulture;
        var mode = ds.ReadXml(myReader);
        var dt = ds.Tables[0];