У меня есть проект, который считывает XML-файл ожидаемых результатов сборки для каждой области сборки и сравнивает его с журналами сборки, чтобы определить, прошла ли сборка. Прямо сейчас linq показывает каждое условие в отдельной строке результатов, но (поскольку я просматриваю каждую строку результатов linq, чтобы проверить успешность) мне нужно иметь возможность проверять оба условия (если их два) в момент в одно и то же время, а не в разное время. В противном случае я мог бы проверять не ту строку файла журнала. Обратите внимание, что иногда существует два критерия успеха, но не всегда. Это всего лишь два условия, которые мне нужно проверить сразу. Чтобы внести это изменение, я переместил тег SuccessCriteria из тега «Условия» в раздел «Условия». Благодаря этому изменению linq не воспринимает строки XML. В моем полном XML-файле должно быть около 69 строк результатов_b, но он включает пару разделов, которые я не показываю, которые я оставил в старом формате. Я попытался изменить cName = (string)p.Attribute("name") на cName = (string)b.Attribute("name") в LINQ, но получил те же результаты. В то же время было изменено значение cValue.
Мой вопрос: как мне изменить LINQ, чтобы получить полный XML-файл, и groupBy_b, чтобы проверить оба условия одновременно? Я хочу объединить условия в список, но вокруг него должны быть другие данные, такие как машина сборки, имя процесса и критерии успеха. Например, таблица, которую я помещаю в xml, для некоторых частей журнала выглядит так: ParseISLogStats (поэтому мне нужно проверить правильность BuildProject и BaseBuildArea в журнале, поскольку это разные столбцы, но критерий успеха только один для этих двух условия):
ParseISLogStats Desctiption=nightly v17 partb Projects=8 Status=Success
ParseISLogStats Description=nightly v17 partb Projects=8 Status=Success
ParseISLogStats Description=nightly v17 Projects=33 Status=Success
Я смотрел на эти примеры: линклинк
На данный момент у меня есть этот код (с добавлением groupBy, чтобы попытаться собрать условия в список):
void ReadXml()
{
int i_results = 0; //0 is successful for all; if it increases we have failed in one
XmlDocument xml = new XmlDocument();
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string newPath = Path.GetFullPath(Path.Combine(path, @"..\.."));
string finalPathXmlb = Path.GetFullPath(Path.Combine(newPath, @"BuildVerificationBuildAttributes_b.xml")); //new format
teamPost = "";
//linq
XDocument xmlDoc2 = XDocument.Load(finalPathXmlb);
//XElement buildVerificationElement = xmlDoc1.Element("BuildVerification");
//IEnumerable<XElement> buildElements = buildVerificationElement.Elements("build");
//IEnumerable<XElement> buildMachines = buildElements.Elements("BuildMachine");
//get just the times from the xml
//var codeFreezeTime = xmlDoc1.Descendants("codeFreezeTime").First()?.Value;
//logToFileAndScreen(codeFreezeTime);
//get build info from xml, such as process (log name), condition, successCriteria, build machine, start time header,
IEnumerable<XElement> processes = buildMachines.Elements("Process");
var results_b =
xmlDoc2.Descendants("build")
.SelectMany(b => b.Descendants("Process")
.SelectMany(p => p.Descendants("Condition")
.SelectMany(c => c.Descendants("SuccessCriteria")
.Select(sc => new
{
buildMach = (string)b.Element("BuildMachine"),
p1 = (string)p.Element("ProcessName"),
startTimeHeader = (string)p.Element("startTimeHeader"),
cName = (string)c.Attribute("name"),
cValue = (string)c.Attribute("value"),
f1 = (string)sc.Element("field"),
c1 = (string)sc.Element("comparison"),
v1 = (string)sc.Element("value")
})))).ToList();
var groups_b = results_b.GroupBy(x => new { build_machine = x.buildMach, process_name = x.p1, name = x.cName }).ToList();
} //ParseLogFile
Я хочу, чтобы мой groups_b выглядел так (но если linq не работает, он никогда этого не сделает, но я не думаю, что у меня все правильно в моем groups_b, если linq действительно работает):
{buildMach = mach64, p1=ParseISLogStats, startTimeHeader = StartTime, {cName=Description, cValue=nightly v17 partb, cName=Projects, cValue=8}, f1=Status, c1=equal, v1=Succcess}
{buildmach = mach64, p1=ParseISLogStats, startTimeheader = StartTime, {cName=Description, cValue = nightly v17, cName=Projects, cValue=33},f1=status, c1=equal, v1=Success}
Вот как выглядит мой XML:
<?xml version = "1.0" encoding = "UTF-8"?>
<BuildVerification>
<codeFreezeTime>19:00</codeFreezeTime>
<build>
<BuildMachine>mach31</BuildMachine>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\R\filename1.cs">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\R\filename2.cs">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\R\filename3.h">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
</build>
<build>
<BuildMachine>mach46</BuildMachine>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\K\filename4.cs">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\K\fiename5.h">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\K\filename6.cs">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\D\filename7.cs">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\K\K\filename8.cs">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\K\K\filename9.h">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\K\K\filename11.cs">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison> <!--can't use >= here -->
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\K\filename22.cs">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinFileVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='VersionFile' value = "\\view\Build_NightlyDeveloper\K\filename33.cs">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinISProjectVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='ProductName' value = "P1">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinISProjectVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='ProductName' value = "P2">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>SpinISProjectVersionStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='ProductName' value = "P3">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>GetSWStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='DestinationPath' value = "D:\Builds\Retail\nightly">
</Condition>
<Condition
name='DestinationPath' value = "D:\Builds\Retail\nightly">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Successful</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>ParseISLogStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='Nightly' value = "D:\nightly V17">
</Condition>
<Condition
name='Projects' value=33>
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>ParseISLogStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='Nightly' value = "D:\nightly V17 partb">
</Condition>
<Condition
name='Projects' value=8>
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>ParseISLogStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='BuildProject' value = "DInstallerBuild">
</Condition>
<Condition
name='BaseBuildArea' value = "D:\R_Builds\K\v2.1\nightly">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
<Process>
<ProcessName>ParseISLogStats</ProcessName>
<startTimeHeader>StartTime</startTimeHeader>
<Conditions>
<Condition
name='BuildProject' value = "KInstallerBuild">
</Condition>
<Condition
name='BaseBuildArea' value = "D:\R_Builds\K\v2.1\nightly">
</Condition>
</Conditions>
<SuccessCriteria>
<field>Status</field>
<comparison>equal</comparison>
<value>Success</value>
</SuccessCriteria>
</Process>
</build>
</BuildVerification>
Обновлять: Я попытался изменить LINQ на следующее, но он по-прежнему просто возвращает данные из XML, который я оставил в старом формате, а не из того, в котором SuccessCriteria находится на том же уровне/глубине, что и Условия.
var results_b = (
from b in xmlDoc2.Descendants("build")
from p in b.Descendants("Process")
from c in p.Descendants("Condition")
from sc in c.Descendants("SuccessCriteria")
select new
{
buildMach = (string)b.Element("BuildMachine"),
p1 = (string)p.Element("ProcessName"),
startTimeHeader = (string)p.Element("startTimeHeader"),
cName = (string)c.Attribute("name"),
cValue = (string)c.Attribute("value"),
f1 = (string)sc.Element("field"),
c1 = (string)sc.Element("comparison"),
v1 = (string)sc.Element("value")
})
.ToList();
да, они читаются как строки из xml.
Несколько предыдущих вопросов, связанных с этим плакатом: здесь , здесь , здесь и здесь. Похоже, та же общая проблема, но структура данных XML изменилась. Элемент SuccessCriteria теперь является дочерним элементом Process вместо Condition. Раньше также были случаи, когда несколько одноуровневых элементов SuccessCriteria, но последний образец больше этого не показывает.
@TN - есть ли способ добавить сюда именованные списки вместо анонимных?





Чтобы перемещаться по XML-данным с помощью запроса LINQ, вам необходимо сначала понять иерархию элементов (схему), а затем адаптировать свой запрос для соответствия.
Из анализа вашего примера XML выяснилось, что ваш текущий контент имеет такую структуру:
BuildVerification
codeFreezeTime (text)
*build
BuildMachine (text)
*Process
ProcessName (text)
startTimeHeader (text)
*Conditions
Condition
@name (text)
@value (text)
*SuccessCriteria
field (text)
comparison (text)
value (text)
(Это не формальная запись, а просто мой способ расположения вещей. Отступы отражают вложенность, «(текст)» указывает на элементы, имеющие текстовое содержимое, «@» указывает на атрибут, а «*» я использовал для обозначения элементов, которые можно повторить.)
Я отметил SuccessCriteria как повторяющийся, поскольку в ваших предыдущих вопросах были примеры, когда элемент Process содержал несколько вложенных элементов SuccessCriteria.
Итак, основная проблема вашего текущего запроса заключается в том, что вы пытаетесь выбрать элементы SuccessCriteria, которые являются дочерними элементами Condition, но если вы просмотрите приведенный выше макет, вы увидите, что этого никогда не произойдет. SuccessCriteria теперь ребенок Process.
Нам также необходимо обрабатывать вложенные коллекции.
Запрошенный вами макет результатов примерно следующий:
{
buildMach = value,
p1 = value,
startTimeHeader = value,
{
cName = value,
cValue = value,
cName = value,
cValue = value
},
f1 = value,
c1 = value,
v1 = value
}
Я считаю, что для обработки нескольких условий для каждого процесса и (потенциально) нескольких критериев для каждого процесса (которые соответствуют этим условиям) вам действительно нужен макет вроде:
List of {
buildMach = value,
p1 = value,
startTimeHeader = value,
Conditions = List of {
cName = value,
cValue = value
},
SuccessCriteria = List of {
f1 = value,
c1 = value,
v1 = value
}
}
Это позволит вам определить правила проверки, каждое из которых может определять несколько условий соответствия, а затем применять несколько проверок критериев успеха.
Чтобы получить вложенную структуру, указанную выше, части Condition и SuccessCriteria вашего запроса необходимо будет переместить в подвыражения конструктора объекта результата.
Для улучшения читаемости я также рекомендую отказаться от синтаксиса приведения (string) и вместо этого использовать свойство .Value для получения содержимого элемента и атрибута. (Если есть вероятность, что элемент или атрибут может отсутствовать или иметь значение NULL, вы можете использовать синтаксис ?.Value, распространяющий значение NULL.)
Результирующий код будет примерно таким:
var results1 =
xmlDoc2.Descendants("build")
.SelectMany(b => b.Descendants("Process")
.Select(p => new
{
buildMach = b.Element("BuildMachine").Value,
p1 = p.Element("ProcessName").Value,
startTimeHeader = p.Element("startTimeHeader").Value,
conditions = p.Descendants("Condition")
.Select(c => new
{
cName = c.Attribute("name").Value,
cValue = c.Attribute("value").Value
})
.ToList(),
successCriteria = p.Descendants("SuccessCriteria")
.Select(sc => new
{
f1 = sc.Element("field").Value,
c1 = sc.Element("comparison").Value,
v1 = sc.Element("value").Value
})
.ToList()
})).ToList();
Эквивалент с использованием синтаксиса запроса LINQ:
var results2 = (
from b in xmlDoc2.Descendants("build")
from p in b.Descendants("Process")
select new {
buildMach = b.Element("BuildMachine").Value,
p1 = p.Element("ProcessName").Value,
startTimeHeader = p.Element("startTimeHeader").Value,
conditions = (
from c in p.Descendants("Condition")
select new
{
cName = c.Attribute("name").Value,
cValue = c.Attribute("value").Value
})
.ToList(),
successCriteria = (
from sc in p.Descendants("SuccessCriteria")
select new
{
f1 = sc.Element("field").Value,
c1 = sc.Element("comparison").Value,
v1 = sc.Element("value").Value
})
.ToList()
})
.ToList();
Результаты (частичные):
...
{"buildMach":"mach46","p1":"ParseISLogStats","startTimeHeader":"StartTime","conditions":[{"cName":"BuildProject","cValue":"DInstallerBuild"},{"cName":"BaseBuildArea","cValue":"D:\\R_Builds\\K\\v2.1\\nightly"}],"successCriteria":[{"f1":"Status","c1":"equal","v1":"Success"}]}
{"buildMach":"mach46","p1":"ParseISLogStats","startTimeHeader":"StartTime","conditions":[{"cName":"BuildProject","cValue":"KInstallerBuild"},{"cName":"BaseBuildArea","cValue":"D:\\R_Builds\\K\\v2.1\\nightly"}],"successCriteria":[{"f1":"Status","c1":"equal","v1":"Success"}]}
См. эту .NET Fiddle.
Возможно, вам будет полезно определить именованные классы для получения результатов вместо использования анонимных типов.
Если вы внесете дальнейшие изменения в дизайн своих данных, я предлагаю вам пересмотреть представленное выше представление макета (схемы) или собрать эквивалентное представление, которое подойдет вам. Затем вы можете ссылаться на эту обновленную схему, чтобы помочь вам при внесении необходимых изменений в ваш код.
Большое спасибо! Я пробую первый и использовал значение ?.Value, потому что у меня есть пара значений, для которых я использую "". Теперь мне просто нужно выяснить, как выполнить цикл foreach со строками результатов для отправки в мой метод синтаксического анализа с объектом списка. Я действительно ценю твою помощь. Я пытался понять это всю неделю. Ваша запись очень полезна, и теперь я понимаю это намного лучше.
Я пытаюсь использовать вложенный список для отправки списка условий в мой метод int ParseLogFile1 (строка codeFreezeTime, строка startTimeHeader, строка serverName, строка logName, условия List<List<string>>, List<List<string>> SuccessCriteria) {} и это при вызове метода: foreach (var cur in results1) { i_results += ParseLogFile1(codeFreezeTime, cur.startTimeHeader, cur.buildMach, currentItem.p1, cur.conditions, cur.successCriteria); } но возникла ошибка преобразования анонимного типа
Если вам нужно передавать эти данные между функциями, вам следует определить именованные классы для Condition, SuccessCriteria и всего ProcessValidationRule (или любого другого имени класса, которое вам понятно), каждый с соответствующими свойствами. Коллекции Condition и SuccessCriteria внутри класса ProcessValidationRule будут иметь типы List<Condition> и List<SuccessCriteria> соответственно. Приведенный выше запрос затем создаст новые и заполнит эти именованные классы вместо текущих безымянных/анонимных классов.
пожалуйста подтвердите. значение условия = 33 и значение = 8 - должно быть в виде строк?