使用linq获取XML对象数组

使用linq获取XML对象数组

问题描述:

我想知道是否有一种方法可以使用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

我们在工作中使用手动滚动的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

这给一个尝试(因为你喜欢的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;} 
}