Улучшение получения данных из XML-файла

#c# #xml #xpath

#c# #xml #xpath

Вопрос:

У меня есть такой XML-файл:

    <Document>
       <Tests Count="4">
          <Test>
             <Name>test with a</Name>
             <Type>VI test</Type>
             <Result>Pass</Result>
          </Test>
          <Test>
             <Name>test plot</Name>
             <Type>Curve test</Type>
             <Result>Fail</Result>
          </Test>
          <Test>
             <Name>test fixture</Name>
             <Type>Leakage test</Type>
             <Result>Pass</Result>
          </Test>      
          <Test>
             <Name>test fixture</Name>
             <Type>Leakage test</Type>
          </Test>             
        </Tests>
   </Document>
  

Я создал класс, содержащий в нем каждый «Тест»:

 class TestGroup
{
    public string TestName { get; set; }
    public string TestType { get; set; }
    public string TestResult { get; set; }
}
  

Поскольку я очень новичок в XML, на данный момент я получаю такие данные (ztr is XmlDocument ):

 public List<TestGroup> GetTestGroups()
        {
            List<TestGroup> TestNode = new List<TestGroup>();

            string[] type_t = new string[GetTestsNumber()];
            string[] name_t = new string[GetTestsNumber()];
            string[] result_t = new string[GetTestsNumber()];

            //name
            string xpath = "/Document/Tests/Test";

            int i = 0;

            foreach (XmlElement Name in ztr.SelectNodes(xpath))
            {
                name_t[i] = Name.SelectSingleNode("Name").InnerText;
                i  ;
            }
            ///////////

            //result
            xpath = "/Document/Tests/Test";

            i = 0;

            foreach (XmlElement Result in ztr.SelectNodes(xpath))
            {
                result_t[i] = Result.SelectSingleNode("Result").InnerText;
                i  ;
            }
            /////////////////
            xpath = "/Document/Tests/Test";

            i = 0;

            foreach (XmlElement Type in ztr.SelectNodes(xpath))
            {
                type_t[i] = Type .SelectSingleNode("Type").InnerText;
                i  ;
            }

            int count = type_t.GetLength(0);

            for (i = 0; i < count; i  )
            {
                LUTestGroup node = new LUTestGroup();
                node.TestName = name_t[i];
                node.TestType = type_t[i];
                node.TestResult = result_t[i];

                TestNode.Add(node);
            }

            return TestNode;
        }
  

Очевидно, что этот способ довольно опасен. Он повторяет отдельные моменты времени во время xml и каждый раз добавляет одно свойство к классу. Также, если вы обратите внимание на пример XML-файла. у последнего Test нет Result узла, поэтому код, который я написал, иногда вылетает, когда я загружаю разные XML-файлы.

Не могли бы вы помочь мне, пожалуйста, написать метод для безопасного заполнения списка всеми тестами, доступными в XML-файле, и если у какого-либо из них нет узла, метод не сработает? и, что очень важно, выполняет работу за ОДИН РАЗ!

Спасибо

Комментарии:

1. Обязательно ли использовать XmlDocument? LINQ to XML сделал бы это намного приятнее, если вы можете использовать .NET 3.5.

2. все мои другие методы также основаны на XmlDocument, на данный момент я не думаю об использовании LINQ. Но было бы неплохо, если бы вы привели пример для этого XML-файла в LINQ, тогда я постараюсь создать свои другие методы на основе этого.

Ответ №1:

(Как указано в комментариях.)

Версия LINQ to XML действительно проста:

 // Could use doc.Root.Element("Tests").Elements("Test") to be explicit
return doc.Descendants("Test")
          .Select(x => new LUTestGroup {
                      TestName = (string) x.Element("Name"),
                      TestType = (string) x.Element("Type"),
                      TestResult = (string) x.Element("Result")
                  })
          .ToList();
  

Обратите внимание, что приведение from XElement to string вернет null, если вы дадите ему нулевую XElement ссылку, поэтому в вашем случае «тест на утечку» вы получите свойство with LUTestGroup с нулевым TestResult значением.

Комментарии:

1. Можете ли вы рассказать plesae, как следует объявлять «doc»? У меня это как «doc = new XPathDocument (filePath);» Я думаю, я должен использовать XDocument, как его определить?

2. @Sean87: использование XDocument doc = XDocument.Load(filePath);

Ответ №2:

Мне кажется, что вы делаете это трудным путем. Пусть система анализирует xml за вас! Вы можете аннотировать классы, чтобы сообщить им, как выглядит xml:

 [XmlRoot("Document")]
public class TestWrapper {
    private readonly List<TestGroup> tests = new List<TestGroup>();
    [XmlArray("Tests"), XmlArrayItem("Test")]
    public List<TestGroup> Tests { get { return tests; } }
}

public class TestGroup {
    [XmlElement("Name")] public string TestName { get; set; }
    [XmlElement("Type")] public string TestType { get; set; }
    [XmlElement("Result")] public string TestResult { get; set; }
}
  

затем:

 TestWrapper doc;
using(var reader = XmlReader.Create(source))
{
    var serializer = new XmlSerializer(typeof (TestWrapper));
    doc = (TestWrapper) serializer.Deserialize(reader);
}
// et voila; loaded! prove it:
foreach(var item in doc.Tests)
{
    Console.WriteLine("{0}: {1}, {2}",
        item.TestName, item.TestType, item.TestResult);
}
  

Вы также можете использовать XmlSerializer для создания xml с тем же макетом, хотя (довольно избыточный, IMO) /Document/Tests/@Count — это боль — я мог бы показать вам, как это исправить, если вам действительно нужно.

Комментарии:

1. Спасибо за советы! Я думаю, как сказал Джон, Linq будет менее головным способом.