2013-09-30 204 views
2

我的項目需要將輸入XML文件轉換爲DataTable的功能。 我正在使用下面的代碼來做到這一點。刪除XML中的重複元素

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> 

正如你可以在上面看到,出現兩次元素方案。當這種XML文件到來時ds.ReadXml(dataSourceFileStream);無法返回正確的數據表。

任何更好的方法來處理?

+0

嘗試使用的ReadXml(流XmlReadMode.IgnoreSchema – Carra

+0

你想將任何輸入XML文件轉換爲DataTable?如果是這樣,那麼這不是一個好主意,因爲XML包含分層數據 –

+0

@Carra我試過那個,它也行不通 – vijay

回答

1

看起來像你必須先修復XML。您可以通過使用XDocument和關聯的類來完成此操作。但首先,你需要建立一個比較基於其名稱的兩個XElements一個EqualityComparer:

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

+0

謝謝,但有沒有一種方法可以使用LINQ來實現同樣的效果? – vijay

+0

你是怎麼意思的?意味着一個正在做你需要的一切的單線程? – AcidJunkie

+0

是的,有點像內聯Linq聲明,只是好奇。無論如何,絆倒用戶名。 :) – vijay

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]; 
+0

謝謝你的回答。 – vijay