【发布时间】:2011-05-12 00:58:21
【问题描述】:
这是我在 StackOverflow 上的第一篇文章,请多多包涵。如果我的代码示例有点长,我先道歉。
使用 C# 和 LINQ,我试图在一个更大的 XML 文件中识别一系列第三级 id 元素(在本例中为 000049)。每个第三级id 都是独一无二的,我想要的那些是基于每个级别的一系列后代信息。更具体地说,如果type == A 和location type(old) == vault 和location type(new) == out,那么我想选择那个id。下面是我正在使用的 XML 和 C# 代码。
一般来说,我的代码有效。如下所述,它将返回 000049 的 id 两次,这是正确的。然而,我发现了一个漏洞。如果我删除第一个包含type == A 的history 块,我的代码仍然会返回两次000049 的id,而它应该只返回一次。我知道为什么会这样,但我想不出更好的方法来运行查询。有没有更好的方法来运行我的查询以获得我想要的输出并仍然使用 LINQ?
我的 XML:
<?xml version="1.0" encoding="ISO8859-1" ?>
<data type="historylist">
<date type="runtime">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
<hour>15</hour>
<minutes>24</minutes>
<seconds>46</seconds>
</date>
<customer>
<id>0001</id>
<description>customer</description>
<mediatype>
<id>kit</id>
<description>customer kit</description>
<volume>
<id>000049</id>
<history>
<date type="optime">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
<hour>03</hour>
<minutes>00</minutes>
<seconds>02</seconds>
</date>
<userid>batch</userid>
<type>OD</type>
<location type="old">
<repository>vault</repository>
<slot>0</slot>
</location>
<location type="new">
<repository>out</repository>
<slot>0</slot>
</location>
<container>0001.kit.000049</container>
<date type="movedate">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
</date>
</history>
<history>
<date type="optime">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
<hour>06</hour>
<minutes>43</minutes>
<seconds>33</seconds>
</date>
<userid>vaultred</userid>
<type>A</type>
<location type="old">
<repository>vault</repository>
<slot>0</slot>
</location>
<location type="new">
<repository>out</repository>
<slot>0</slot>
</location>
<container>0001.kit.000049</container>
<date type="movedate">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
</date>
</history>
<history>
<date type="optime">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
<hour>06</hour>
<minutes>43</minutes>
<seconds>33</seconds>
</date>
<userid>vaultred</userid>
<type>S</type>
<location type="old">
<repository>vault</repository>
<slot>0</slot>
</location>
<location type="new">
<repository>out</repository>
<slot>0</slot>
</location>
<container>0001.kit.000049</container>
<date type="movedate">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
</date>
</history>
<history>
<date type="optime">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
<hour>06</hour>
<minutes>45</minutes>
<seconds>00</seconds>
</date>
<userid>batch</userid>
<type>O</type>
<location type="old">
<repository>out</repository>
<slot>0</slot>
</location>
<location type="new">
<repository>site</repository>
<slot>0</slot>
</location>
<container>0001.kit.000049</container>
<date type="movedate">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
</date>
</history>
<history>
<date type="optime">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
<hour>11</hour>
<minutes>25</minutes>
<seconds>59</seconds>
</date>
<userid>ihcmdm</userid>
<type>A</type>
<location type="old">
<repository>out</repository>
<slot>0</slot>
</location>
<location type="new">
<repository>site</repository>
<slot>0</slot>
</location>
<container>0001.kit.000049</container>
<date type="movedate">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
</date>
</history>
<history>
<date type="optime">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
<hour>11</hour>
<minutes>25</minutes>
<seconds>59</seconds>
</date>
<userid>ihcmdm</userid>
<type>S</type>
<location type="old">
<repository>out</repository>
<slot>0</slot>
</location>
<location type="new">
<repository>site</repository>
<slot>0</slot>
</location>
<container>0001.kit.000049</container>
<date type="movedate">
<year>2011</year>
<month>04</month>
<day>22</day>
<dayname>Friday</dayname>
</date>
</history>
</volume>
...
我的 C# 代码:
IEnumerable<XElement> 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" 和"movedate" 日期用于选择history 用于选择id。那有意义吗?我希望以这样的方式结束:
select new {
id = volume.Element("id").Value,
// this is from "optime"
opYear = <whaterver>("year").Value,
opMonth = <whatever>("month").Value,
opDay = <whatever>("day").Value,
// this is from "movedate"
mvYear = <whaterver>("year").Value,
mvMonth = <whatever>("month").Value,
mvDay = <whatever>("day").Value
}
我尝试了很多不同的组合,但<date type="optime"> 和<date type="movedate"> 的Attributes 一直妨碍我,我似乎无法得到我想要的。
好的。我找到了一个很好用的solution:
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
};
但是,当它找到没有"movedate" 的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 帮助的要求,并获取了必要的额外日期信息。通过使用三元运算符?:,它还解释了"movedate" 没有元素的少数情况。
现在,如果有人知道如何提高效率,我仍然很感兴趣。谢谢。
【问题讨论】:
标签: c# .net xml linq linq-to-xml