使用linq从XML中获取对象数组

时间:2012-03-13 14:36:19

标签: c# xml linq

我想知道是否有一种方法可以使用Linq to XML从xml字符串中获取对象数组... 我习惯使用XPath来完成我的所有脏工作,而Xpath似乎非常直观。但是,我一直听说linq是多么伟大,一旦你得到它,它就会让你的生活更轻松。陪审团仍然在那个问题上,但是再一次,我对Linq并不是那么好。还在学习。但是假设我有一个对象......

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 / templates / template将是我上面的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的基础

2 个答案:

答案 0 :(得分:2)

尝试一下(因为你喜欢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;}
}

答案 1 :(得分:1)

我们在工作时使用手工制作的linq-to-xml,所以我会摇摆。

一个简单的说明是,所有EMPIID元素都有一个值=“”,这会在调用FormatException时产生int.ParseXAttribute上有一系列显式的强制转换操作符,可以优雅地使用常见的.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,则无需更正类型例如转换。我使用elements关键字引入了范围变量let;这对于仅计算一次值并重复使用非常有用。由于TemplateModel类型的TemplateModel属性类型是列表,因此我调用ToList扩展名来设置属性。我调用了cast操作符来将属性转换为intstring,而不是调用int.ParseToString

LINQ的功能允许以非常优雅的方式进一步处理(过滤,折叠和投影)。能够从linq-to-xml无缝转换到linq-to-objects的工作,使得LINQ非常引人注目,恕我直言。无论如何,进入LINQ游泳池,水很好。 :)