2013-09-30 4 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 и связанные классы. Но сначала нужно создать 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

+0

Спасибо, Но есть ли способ, которым мы можем добиться того же, используя LINQ? – vijay

+0

Как вы это понимаете? что означает однострочный, который делает все, что вам нужно? – AcidJunkie

+0

Да, немного похоже на inline заявление 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

Смежные вопросы