2012-03-13 126 views
0

我想知道是否有一種方法可以使用Linq將XML從xml字符串中取出對象數組... 我習慣於使用XPath來執行我所有的骯髒的工作,和Xpath似乎很直觀。但是,我一直聽說linq有多棒,一旦你掌握了它,那麼它會讓你的生活變得更輕鬆。陪審團仍然在那一個,但是,然後再次,我對Linq不是那麼好。仍在學習。但是假設我有一個對象...使用linq獲取XML對象數組

class PatientClass 
{ 
    //public int Item_ID { get; set; } 
    public int PatientId { get; set; } 
    public int EMPIID { get; set; } 
} 

假設我有另一個對象...

public class TemplateModel 
{ 
    List<PatientACOModel> Template { set; get; } 
} 

這簡直就是第一對象的列表...

假設我有一個XML文件,看起來像這樣...

<dataTemplateSpecification id="id1" name="name1" > 
<templates xmlns=""> 
<template> 
    <elements> 
    <element id="element0" name="PatientId" display="Patient ID" dataType="String" visable="true" readOnly="false" value="4563"> 
     <mapping path="//Template/TemplateData/ACOData/PATIENT_ID" /> 
    </element> 
    <element id="element1" name="PopulationPatientID" display="Population Patient ID" dataType="String" visable="true" readOnly="true" enc="2098" value="6407"> 
     <mapping path="//Template/TemplateData/ACOData/POPULATION_PATIENT_ID" /> 
    </element> 
    <element id="element2" name="EMPIID" display="EMPIID" dataType="String" visable="true" readOnly="true" value=""> 
     <mapping path="//Template/TemplateData/ACOData/EMPI" /> 
    </element>   
    </elements> 
</template> 
<template> 
    <elements> 
    <element id="element0" name="PatientId" display="Patient ID" dataType="String" visable="true" readOnly="false" value="4563"> 
     <mapping path="//Template/TemplateData/ACOData/PATIENT_ID" /> 
    </element> 
    <element id="element1" name="PopulationPatientID" display="Population Patient ID" dataType="String" visable="true" readOnly="true" enc="2098" value="6407"> 
     <mapping path="//Template/TemplateData/ACOData/POPULATION_PATIENT_ID" /> 
    </element> 
    <element id="element2" name="EMPIID" display="EMPIID" dataType="String" visable="true" readOnly="true" value=""> 
     <mapping path="//Template/TemplateData/ACOData/EMPI" /> 
    </element>   
    </elements> 
</template> 
<template> 
    <elements> 
    <element id="element0" name="PatientId" display="Patient ID" dataType="String" visable="true" readOnly="false" value="4563"> 
     <mapping path="//Template/TemplateData/ACOData/PATIENT_ID" /> 
    </element> 
    <element id="element1" name="PopulationPatientID" display="Population Patient ID" dataType="String" visable="true" readOnly="true" enc="2098" value="6407"> 
     <mapping path="//Template/TemplateData/ACOData/POPULATION_PATIENT_ID" /> 
    </element> 
    <element id="element2" name="EMPIID" display="EMPIID" dataType="String" visable="true" readOnly="true" value=""> 
     <mapping path="//Template/TemplateData/ACOData/EMPI" /> 
    </element>   
    </elements> 
</template> 
</templates> 
</dataTemplateSpecification> 

你看,dataTemplateSpecification /模板/模板將是一個我上面的PatientClass的實例。而dataTemplateSpecification/templates /將是TemplateModel對象的一個​​實例(PatientClasses的一個列表...我在List中將它們命名爲PatientACOModel,但它們基本上是同一個東西......一個變量沒有像另一個)。

現在我使用它來解析出病人對象...

IEnumerable<PatientClass> template = (IEnumerable<PatientClass>)(from templates in xDocument.Descendants("dataTemplateSpecification")//elem.XPathSelectElements(string.Format("//templates/template[./elements/element[@name=\"PopulationPatientID\"and @value='{0}' and @enc='{1}']]", "1", 0)) 
               select new PatientClass 
               { 
                PatientId = int.Parse(templates.Descendants("element").Single(el => el.Attribute("name").Value=="PatientId").ToString()),//XPathSelectElement("elements/element[@name='PatientId']").Attribute("value").Value), 
                EMPIID = int.Parse(templates.Descendants("element").Single(el => el.Attribute("name").Value=="EMPIID").ToString()),//XPathSelectElement("elements/element[@name='EMPIID']").Attribute("value").Value), 
               } 

這是目前返回空值,但我的工作對...但我怎樣才能使這些病人的列表。我可能需要一個超級查詢來處理東西和子查詢列表以獲取患者信息的權利?

因此,像這樣......

IEnumerable<TemplateModel> template = (IEnumerable<TemplateModel>)(from templates in elem.XPathSelectElements("//templates/template") 
               select new TemplateModel 
               { 
                TemplateModel = 
                (from pat in templates 
                 select new PatientClass 
                 { 
                  PatientId = int.Parse(templates.XPathSelectElement("elements/element[@name='PatientId']").Attribute("value").Value), 
                  EMPIID = int.Parse(templates.XPathSelectElement("elements/element[@name='EMPIID']").Attribute("value").Value), 
                ) 
                } 

這似乎合乎邏輯的我。但也許我不明白Linq的基礎

+0

是有一些原因,你不只是序列化在你的對象標準方式? – 2012-03-13 14:39:38

+0

這將是一個嚴重醜陋的對象。出於某種原因,我的建築師是這種XML模式的忠實粉絲。這可能與我們所在的行業有關。 – SoftwareSavant 2012-03-13 15:11:49

+0

一種可能性是使用XSL將數據從元數據規範轉換爲更具體的某個元素(即一個名爲PatientID的元素,其值爲any,而不是規範要被稱爲PatientID的元素)。然後,您可以使用XML序列化將其轉換爲對象。但我猜這是元數據到具體的任何步驟,這是殺手,對吧? – 2012-03-13 16:00:50

回答

1

我們在工作中使用手動滾動的linq-to-xml,所以我會擺擺步伐。

一個簡單的提示是所有的EMPIID元素都有一個值=「」,當調用int.Parse時將導致FormatException。在XAttribute上有一系列明確的投射運算符,它們可以很好地幫助您使用常見的.net類型。

下面是我可能會爲你需要做解析寫(保持純淨解析的xnode樣式而不使用XPath)代碼:

IEnumerable<TemplateModel> templates = 
    from dataTemplate in xDocument.Descendants("dataTemplateSpecification") 
    select new TemplateModel 
    { 
     TemplateModel = 
      (from template in dataTemplate.Element("templates").Elements("template") 
      let elements = template.Element("elements").Elements("element") 
      select new PatientClass 
      { 
       PatientId = (int)elements.Single(e => (string)e.Attribute["name"] == "PatientId").Attribute("value"), 
       EMPIID = (int)elements.Single(e = (string)e.Attribute["name"] == "EMPIID").Attribute("value"), 
      }).ToList() 
    }; 

類型的查詢已經IEnumerable<TemplateModel>所以沒有立即需要投下結果。在大多數情況下,我的首選是簡單地將類型聲明爲var,但您的口味可能不同 - 將其保留爲var意味着如果我應用分組或其他LINQ轉換,則不必更正類型。我使用let關鍵字引入了範圍變量elements;這對計算一次數值並重新使用它非常有用。由於TemplateModel類型的TemplateModel屬性的類型是一個列表,因此我調用ToList擴展名來設置該屬性。我打電話給鑄造操作員的屬性將它們的值轉換爲intstring,而不是調用int.ParseToString

LINQ的功能允許以非常優雅的方式進一步處理(過濾,摺疊和投影)。能夠無縫地從linq-to-xml轉換到linq-to-objects,使得LINQ非常引人注目,恕我直言。總之,進入LINQ池,水很好。 :)

+0

很好的答案。但是當有空字符串時,我總是得到一個 輸入字符串的格式不正確。例外。對於DateTime屬性同樣的東西 我想,我會用空字符串做什麼? – SoftwareSavant 2012-03-13 18:21:38

+0

您可能會嘗試將值類型更改爲可爲null的類型,例如:分別爲'int?'又名'可空'和'DateTime?'。 XAttribute也具有這些類型的轉換運算符。 – devgeezer 2012-03-13 19:50:19

1

這給一個嘗試(因爲你喜歡的XPath):

var s="<dataTemplateSpecification .../>"; 

var element = XElement.Parse(s); 

var patients = element.XPathSelectElements("//elements").Select (
    e => new Patient 
     { 
      PatientId = (int)e.XPathSelectElement("//element[@id='element0']").Attribute("value"), 
      PopulationPatientId = (int)e.XPathSelectElement("//element[@id='element1']").Attribute("value"), 
      EmpId = (string)e.XPathSelectElement("//element[@id='element2']").Attribute("value"), 
     } 
    ); 

當病人類:

class Patient{ 
    public int PatientId{get;set;} 
    public int PopulationPatientId{get;set;} 
    public string EmpId{get;set;} 
}