Это моя первая публикация на StackOverflow, поэтому, пожалуйста, терпите меня. И я заранее прошу прощения, если мой пример кода немного длинен.
Используя C # и LINQ, я пытаюсь идентифицировать серию элементов третьего уровня id
(в данном случае 000049) в гораздо более крупном XML файл. Каждый третий уровень id
уникален, и те, которые мне нужны, основаны на серии информации о потомках для каждого. В частности, если type == A
и тип местоположения (старый) == хранилище
и тип местоположения (новый) == out
, то я хочу выбрать это ] id
. Ниже приведен код XML и C #, который я использую.
В целом мой код работает. Как написано ниже, он дважды вернет id
000049, что является правильным. Однако я обнаружил глюк. Если я удалю первый блок history
, содержащий type == A
, мой код по-прежнему возвращает id
000049 дважды, тогда как он должен возвращать его только один раз. Я знаю, почему это происходит, но я не могу придумать лучший способ выполнить запрос. Есть ли лучший способ выполнить мой запрос, чтобы получить желаемый результат и по-прежнему использовать LINQ?
Мой XML:
2011
04
22
Friday
15
24
46
0001
customer
kit
customer kit
000049
2011
04
22
Friday
03
00
02
batch
OD
vault
0
out
0
0001.kit.000049
2011
04
22
Friday
2011
04
22
Friday
06
43
33
vaultred
A
vault
0
out
0
0001.kit.000049
2011
04
22
Friday
2011
04
22
Friday
06
43
33
vaultred
S
vault
0
out
0
0001.kit.000049
2011
04
22
Friday
2011
04
22
Friday
06
45
00
batch
O
out
0
site
0
0001.kit.000049
2011
04
22
Friday
2011
04
22
Friday
11
25
59
ihcmdm
A
out
0
site
0
0001.kit.000049
2011
04
22
Friday
2011
04
22
Friday
11
25
59
ihcmdm
S
out
0
site
0
0001.kit.000049
2011
04
22
Friday
...
Мой код C #:
IEnumerable caseIdLeavingVault =
from volume in root.Descendants("volume")
where
(from type in volume.Descendants("type")
where type.Value == "A"
select type).Any() &&
(from locationOld in volume.Descendants("location")
where
((String)locationOld.Attribute("type") == "old" &&
(String)locationOld.Element("repository") == "vault") &&
(from locationNew in volume.Descendants("location")
where
((String)locationNew.Attribute("type") == "new" &&
(String)locationNew.Element("repository") == "out")
select locationNew).Any()
select locationOld).Any()
select volume.Element("id");
...
foreach (XElement volume in caseIdLeavingVault)
{
Console.WriteLine(volume.Value.ToString());
}
Спасибо.
Хорошо, ребята, я снова в тупике. Учитывая эту же ситуацию и приведенное ниже решение @ Elian (которое отлично работает), мне нужны даты "optime"
и "moveate"
для истории
, используемых для выбора идентификатор
. Имеет ли это смысл? Я надеялся закончить чем-то вроде этого:
select new {
id = volume.Element("id").Value,
// this is from "optime"
opYear = ("year").Value,
opMonth = ("month").Value,
opDay = ("day").Value,
// this is from "movedate"
mvYear = ("year").Value,
mvMonth = ("month").Value,
mvDay = ("day").Value
}
Я пробовал так много разных комбинаций, но атрибут
s для
и
все время мешает мне, и я не могу получить то, что хочу.
Хорошо. Я нашел решение , которое работает хорошо:
select new {
caseId = volume.Element("id").Value,
// this is from "optime"
opYear = volume.Descendants("date").Where(t => t.Attribute("type").Value == "optime").First().Element("year").Value,
opMonth = volume.Descendants("date").Where(t => t.Attribute("type").Value == "optime").First().Element("month").Value,
opDay = volume.Descendants("date").Where(t => t.Attribute("type").Value == "optime").First().Element("day").Value,
// this is from "movedate"
mvYear = volume.Descendants("date").Where(t => t.Attribute("type").Value == "movedate").First().Element("year").Value,
mvMonth = volume.Descendants("date").Where(t => t.Attribute("type").Value == "movedate").First().Element("month").Value,
mvDay = volume.Descendants("date").Where(t => t.Attribute("type").Value == "movedate").First().Element("day").Value
};
Однако оно терпит неудачу, когда находит id
без "перемещенной"
. Некоторые из них существуют, так что сейчас я работаю над этим.
Ну, вчера вечером я, наконец, нашел решение, которое искал:
var caseIdLeavingSite =
from volume in root.Descendants("volume")
where volume.Elements("history").Any(
h => h.Element("type").Value == "A" &&
h.Elements("location").Any(l => l.Attribute("type").Value == "old" && ((l.Element("repository").Value == "site") ||
(l.Element("repository").Value == "init"))) &&
h.Elements("location").Any(l => l.Attribute("type").Value == "new" && l.Element("repository").Value == "toVault")
)
select new {
caseId = volume.Element("id").Value,
opYear = volume.Descendants("date").Where(t => t.Attribute("type").Value == "optime").First().Element("year").Value,
opMonth = volume.Descendants("date").Where(t => t.Attribute("type").Value == "optime").First().Element("month").Value,
opDay = volume.Descendants("date").Where(t => t.Attribute("type").Value == "optime").First().Element("day").Value,
mvYear = (volume.Descendants("date").Where(t => t.Attribute("type").Value == "movedate").Any() == true) ?
(volume.Descendants("date").Where(t => t.Attribute("type").Value == "movedate").First().Element("year").Value) : "0",
mvMonth = (volume.Descendants("date").Where(t => t.Attribute("type").Value == "movedate").Any() == true) ?
(volume.Descendants("date").Where(t => t.Attribute("type").Value == "movedate").First().Element("month").Value) : "0",
mvDay = (volume.Descendants("date").Where(t => t.Attribute("type").Value == "movedate").Any() == true) ?
(volume.Descendants("date").Where(t => t.Attribute("type").Value == "movedate").First().Element("day").Value) : "0"
};
Оно удовлетворяет требованиям, с которыми помог @Elian, и получает дополнительную необходимую информацию о дате. Это также объясняет те немногие случаи, когда нет элемента для "moveate"
с использованием тернарного оператора ?:
.
Теперь, если кто-нибудь знает, как сделать это более эффективным , Мне все еще интересно. Спасибо.