2

我的项目需要将输入 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);无法返回正确的数据表。

有更好的方法来处理这个吗?

4

2 回答 2

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];
于 2013-10-01T15:30:43.227 回答
1

看起来你必须先修复 XML。您可以通过使用 XDocument 和相关的类来做到这一点。但首先您需要创建一个 EqualityComparer,它根据名称比较两个 XElement:

    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

于 2013-09-30T14:40:12.840 回答