Самый быстрый способ для анализа файлов XML в C #? - PullRequest
4 голосов
/ 15 июня 2010

Мне нужно загрузить много файлов XML из интернета.Но для тестирования с большей скоростью я скачал все из них (более 500 файлов) следующего формата.

<player-profile>
  <personal-information>
    <id>36</id>
    <fullname>Adam Gilchrist</fullname>
    <majorteam>Australia</majorteam>
    <nickname>Gilchrist</nickname>
    <shortName>A Gilchrist</shortName>
    <dateofbirth>Nov 14, 1971</dateofbirth>
    <battingstyle>Left-hand bat</battingstyle>
    <bowlingstyle>Right-arm offbreak</bowlingstyle>
    <role>Wicket-Keeper</role>
    <teams-played-for>Western Australia, New South Wales, ICC World XI, Deccan Chargers, Australia</teams-played-for>
    <iplteam>Deccan Chargers</iplteam>
  </personal-information>
  <batting-statistics>
    <odi-stats>
      <matchtype>ODI</matchtype>
      <matches>287</matches>
      <innings>279</innings>
      <notouts>11</notouts>
      <runsscored>9619</runsscored>
      <highestscore>172</highestscore>
      <ballstaken>9922</ballstaken>
      <sixes>149</sixes>
      <fours>1000+</fours>
      <ducks>0</ducks>
      <fifties>55</fifties>
      <catches>417</catches>
      <stumpings>55</stumpings>
      <hundreds>16</hundreds>
      <strikerate>96.95</strikerate>
      <average>35.89</average>
    </odi-stats>
    <test-stats>
      .
      .
      .
    </test-stats>
    <t20-stats>
      .
      .
      .    
    </t20-stats>
    <ipl-stats>
      .
      .
      . 
    </ipl-stats>
  </batting-statistics>
  <bowling-statistics>
    <odi-stats>
      <matchtype>ODI</matchtype>
      <matches>378</matches>
      <ballsbowled>58</ballsbowled>
      <runsgiven>64</runsgiven>
      <wickets>3</wickets>
      <fourwicket>0</fourwicket>
      <fivewicket>0</fivewicket>
      <strikerate>19.33</strikerate>
      <economyrate>6.62</economyrate>
      <average>21.33</average>
    </odi-stats>
    <test-stats>
      .
      .
      . 
    </test-stats>
    <t20-stats>
      .
      .
      . 
    </t20-stats>
    <ipl-stats>
      .
      .
      . 
    </ipl-stats>
  </bowling-statistics>
</player-profile>

Я использую

XmlNodeList list = _document.SelectNodes("/player-profile/batting-statistics/odi-stats");

И затем зациклите этот список с помощью foreach as

foreach (XmlNode stats in list)
  {
     _btMatchType = GetInnerString(stats, "matchtype"); //it returns null string if node not availible
     .
     .
     .
     .
     _btAvg = Convert.ToDouble(stats["average"].InnerText);
  }

Даже когда я загружаю все файлы в автономном режиме, синтаксический анализ очень медленный. Есть ли какой-нибудь хороший более быстрый способ их анализа?Или это проблема с SQL?Я сохраняю все извлеченные данные из XML в базу данных, используя DataSets, TableAdapters с командой вставки.

EDIT: Теперь для использования XmlReader, приведите некоторый код XmlReader для вышеуказанного документа.на данный момент я сделал это

void Load(string url) 
{
    _reader = XmlReader.Create(url); 
    while (_reader.Read()) 
    { 
    } 
} 

Доступные методы для XmlReader сбивают с толку.Что мне нужно, чтобы получить статистику ватина и боулинга полностью, характеристики ватина и боулинга разные, в то время как odi, t2o, ipl и т. Д. Внутри боулинга и ватинга одинаковы.

Ответы [ 8 ]

9 голосов
/ 15 июня 2010

Вы можете использовать XmlReader только для быстрой перемотки вперед.

7 голосов
/ 16 июня 2010

Затраты на создание исключений, вероятно, превосходят затраты на синтаксический анализ XML. Вам нужно переписать свой код, чтобы он не генерировал исключения.

Один из способов - проверить наличие элемента, прежде чем запрашивать его значение. Это будет работать, но кода много. Другой способ сделать это - использовать карту:

Dictionary<string, string> map = new Dictionary<string, string>
{
  { "matchtype", null },
  { "matches", null },
  { "ballsbowled", null }
};

foreach (XmlElement elm in stats.SelectNodes("*"))
{
   if (map.ContainsKey(elm.Name))
   {
      map[elm.Name] = elm.InnerText;
   }
}

Этот код будет обрабатывать все элементы, имена которых вам нужны, и игнорировать те, которые вам не нужны. Если значение на карте равно нулю, это означает, что элемент с таким именем не существовал (или не имел текста).

На самом деле, если вы помещаете данные в DataTable, а имена столбцов в DataTable совпадают с именами элементов в XML, вам даже не нужно создавать карту, поскольку свойство DataTable.Columns - это вся карта, которая вам нужна. Кроме того, поскольку DataColumn знает, какой тип данных он содержит, вам не нужно дублировать эти знания в своем коде:

foreach (XmlElement elm in stats.SelectNodes("*"))
{
   if (myTable.Columns.Contains(elm.Name))
   {
      DataColumn c = myTable.Columns[elm.Name];
      if (c.DataType == typeof(string))
      {          
         myRow[elm.Name] = elm.InnerText;
         continue;
      }
      if (c.DataType == typeof(double))
      {
         myRow[elm.Name] = Convert.ToDouble(elm.InnerText);
         continue;
      }
      throw new InvalidOperationException("I didn't implement conversion logic for " + c.DataType.ToString() + ".");
   }
}

Обратите внимание, что я не объявляю переменные для хранения этой информации, поэтому у меня нет шансов испортить и объявить переменную типа данных, отличного от столбца, в котором она хранится, или создать столбец в моей таблице забыв реализовать логику, которая его наполняет.

Редактировать

Хорошо, вот что-то немного хитрое. Это довольно распространенная техника в Python; в C # я думаю, что большинство людей все еще думают, что в этом есть что-то странное.

Если вы посмотрите на второй пример, который я привел, вы увидите, что он использует метаинформацию в DataColumn, чтобы выяснить, какую логику использовать для преобразования значения элемента из текста в его базовый тип. Вы можете сделать то же самое, создав свою собственную карту, например ::1010

Dictionary<string, Type> typeMap = new Dictionary<string, Type>
{
   { "matchtype", typeof(string) },
   { "matches", typeof(int) },
   { "ballsbowled", typeof(int) }
}

и затем сделайте то же самое, что я показал во втором примере:

if (typeMap[elm.Name] == typeof(int))
{
   result[elm.Name] = Convert.ToInt32(elm.Text);
   continue;
}

Ваши результаты больше не могут быть Dictionary<string, string>, так как теперь они могут содержать вещи, которые не являются строками; они должны быть Dictionary<string, object>.

Но эта логика кажется немного неуклюжей; Вы тестируете каждый элемент несколько раз, есть continue операторов, которые можно вырвать - это не страшно, но может быть более кратким. Как? Используя другую карту, отображающую типы в функции преобразования:

Dictionary<Type, Func<string, object>> conversionMap = 
   new Dictionary<Type, Func<string, object>>
{
   { typeof(string), (x => x) },
   { typeof(int), (x => Convert.ToInt32(x)) },
   { typeof(double), (x => Convert.ToDouble(x)) },
   { typeof(DateTime), (x => Convert.ToDateTime(x) }
};

Это немного сложно читать, если вы не привыкли к лямбда-выражениям. Тип Func<string, object> определяет функцию, которая принимает string в качестве аргумента и возвращает объект. И это то, что значения на этой карте: это лямбда-выражения, то есть функции. Они принимают строковый аргумент (x) и возвращают объект. (Откуда мы знаем, что x является строкой? Func<string, object> говорит нам.)

Это означает, что преобразование элемента может занять одну строку кода:

result[elm.Name] = conversionMap[typeMap[elm.Name]](elm.Text);

Переход от внутреннего к внешнему выражению: он ищет тип элемента в typeMap, а затем ищет функцию преобразования в conversionMap и вызывает эту функцию, передавая elm.Text в качестве аргумента.

Это может быть не идеальный подход в вашем случае. Я действительно не знаю. Я показываю это здесь, потому что есть большая проблема в игре. Как отмечает Стив Макконнелл в Code Complete , отладку данных легче, чем отладку кода. Этот метод позволяет вам превратить логику программы в данные. Есть случаи, когда использование этой техники значительно упрощает структуру вашей программы. Это стоит понять.

2 голосов
/ 15 июня 2010

Вы можете попробовать LINQ to XML .Или вы можете использовать this , чтобы выяснить, что использовать.

0 голосов
/ 15 июня 2010

Если вы уже преобразуете эту информацию в DataSet, чтобы вставить ее в таблицы, просто используйте DataSet.ReadXML () - и работайте с таблицами по умолчанию, которые он создает из данных.

Это игрушечное приложение делает это, и оно работает с форматом, который вы определили выше.

Файл проекта: http://www.dot -dash-dot.com / files / wtfxml.zip Установщик: http://www.dot -dash-dot.com / files / WTFXMLSetup_1_8_0.msi

Позволяет просматривать и редактировать XML-файл, используя формат дерева и сетки - таблицы, перечисленные в сетке, являются автоматически созданными DataSet после ReadXML ().

0 голосов
/ 15 июня 2010

XmlReader - решение вашей проблемы.XmlDocument хранит много метаинформации, облегчая доступ к Xml, но он становится слишком загруженным в памяти.Я видел, как некоторые Xmls размером менее 50 КБ были преобразованы в несколько МБ (10 или что-то) из XmlDocument.

0 голосов
/ 15 июня 2010

Если вы знаете , что XML согласован и правильно сформирован, вы можете просто избежать реального анализа XML и просто обработать их как текстовые файлы. Это рискованно, непереносимо и хрупко.

Но это будет самое быстрое (для запуска, а не для кодирования) решение.

0 голосов
/ 15 июня 2010

Я бы не сказал, что LINQ - лучший подход.Я искал в Google и увидел некоторые ссылки на HTML Agility Pack .

Я думаю, что если у вас будет узкое место в скорости, это будет происходить с вашим процессом загрузки.Другими словами, кажется, что ваши проблемы с производительностью не связаны с вашим XML-кодом.Я думаю, что есть способы улучшить вашу скорость загрузки или ваш файл ввода / вывода, но я не знаю, какими они будут.

0 голосов
/ 15 июня 2010

Если документы большие, то потоковый анализатор (который подходит для ваших нужд) будет быстрее, чем использование XmlDocument, в основном из-за меньших накладных расходов.Ознакомьтесь с документацией по XmlReader.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...