2011-04-25 2 views
9

이것은 StackOverflow의 첫 번째 게시물이므로 나와 함께하시기 바랍니다. 내 코드 예제가 조금 길면 사과드립니다.C# 및 LINQ를 사용하여 XML 내부의 정보를 어떻게 추출합니까?

C# 및 LINQ를 사용하여 훨씬 큰 XML 파일에서 세 번째 수준 인 id 요소 (이 경우 000049)를 식별하려고합니다. 각 세 번째 수준 인 id은 고유하며 내가 원하는 것은 각각에 대한 일련의 하위 정보를 기반으로합니다. 더 구체적으로는 type == Alocation type(old) == vaultlocation type(new) == out 인 경우 해당 id을 선택하고 싶습니다. 아래는 XML과 C# 코드입니다.

일반적으로 내 코드가 작동합니다. 아래에 설명 된대로 id이 000049로 두 번 반환됩니다. 그러나, 나는 결함을 발견했다. type == A이 포함 된 첫 번째 history 블록을 제거하면 내 코드는 id 000049를 두 번 반환합니다. 단 한번 반환해야합니다. 왜 그런 일이 벌어지는 지 알지만 쿼리를 실행하는 더 좋은 방법을 알 수는 없습니다. 원하는 출력을 얻고 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()); 
} 

감사합니다.


OK 얘들 아, 나는 다시 곤경에 빠진다. 아래의 위와 같은 상황과 @ Elian의 해결책을 생각해 보면 ("optime""movedate") historyid을 선택하는 데 필요한 날짜가 필요합니다. 말이 돼?

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">에 대한 Attribute의 내 방식으로 점점 유지하고 내가 원하는 것을 얻을 수없는 것 :이 같은 뭔가 종료 기대했다.


확인. 나는 잘 작동하는 solution을 발견 : 그것은 더 "movedate"와 함께 id을 발견하면

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 
}; 

그러나 실패 않습니다. 이들 중 몇 가지가 존재하므로, 지금은 그것에 대해 연구 중입니다.

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"에 대한 요소가없는 몇 가지 인스턴스를 설명합니다.

이제 누가이 방법을 더 효율적으로 만들 수 있는지 알고 있다면 나는 아직도 관심이 있습니다. 감사.

답변

8

난 당신이 뭔가를하려는 생각 :

IEnumerable<XElement> caseIdLeavingVault = 
    from volume in document.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 == "vault") && 
      h.Elements("location").Any(l => l.Attribute("type").Value == "new" && l.Element("repository").Value == "out") 
     ) 
    select volume.Element("id"); 

볼륨이 <history> 유형 A의 요소와 필요한 <location> 요소가 (동일한 필요는 없다) <history> 요소가있는 경우 귀하의 코드는 독립적으로 확인합니다.

위의 코드는 A 유형이고 필요한 <location> 요소를 포함하는 <history> 요소가 있는지 확인합니다.

업데이트 : Abatishchev는 LINQ to XML 대신 xpath 쿼리를 사용하는 솔루션을 제안했지만 쿼리가 너무 단순하고 요청한 것과 정확히 일치하지 않습니다. 다음 XPath 쿼리는 트릭을 할 것입니다, 그러나 그것은 또한 조금 더있다 :

data/customer/mediatype/volume[history[type = 'A' and location[@type = 'old' and repository = 'vault'] and location[@type = 'new' and repository = 'out']]]/id 
+0

@Elian 감사합니다. 나는 그것을 시도 할 것이다. – meffordm

+0

@Elian 작동하는 것 같습니다. 고마워, 또! – meffordm

+0

@meffordm :이 정답을 받아들이는 것을 잊지 마세요. – abatishchev

1

당신이 간단한 XPath 쿼리 사용할 수 있습니다 때 XML 쿼리에 같은 복잡하고 비용이 LINQ를 사용하는 일에 대해 :

using System.Xml; 

string xml = @"..."; 
string xpath = "data/customer/mediatype/volume/history/type[text()='A']/../location[@type='old' or @type='new']/../../id"; 

var doc = new XmlDocument(); 
doc.LoadXml(xml); // or use Load(path); 

var nodes = doc.SelectNodes(xpath); 

foreach (XmlNode node in nodes) 
{ 
    Console.WriteLine(node.InnerText); // 000049 
} 

하거나 XML의 DOM 모델이 필요하지 않은 경우 : 답에 대한

using System.Xml.XPath; 

XPathDocument doc = null; 
using (var stream = new StringReader(xml)) 
{ 
    doc = new XPathDocument(stream); // specify just path to file if you have such one 
} 
var nav = doc.CreateNavigator(); 
XPathNodeIterator nodes = (XPathNodeIterator)nav.Evaluate(xpath); 
foreach (XPathNavigator node in nodes) 
{ 
    Console.WriteLine(node.Value); 
} 
+0

+1; 때로는 네이티브 쿼리가 답입니다. –

+0

xpath 쿼리는이 경우 더 짧을 것이라고 생각하지만 xpath 쿼리는 동일한 작업을 수행하지 않습니다. –

+0

@Elian : 아마도 그렇지는 않습니다. XPath가 좋지는 않지만 일반적으로 아이디어를 보았습니다. – abatishchev

관련 문제