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

本文关键字:获取 一个对象 数组 XML linq 使用 | 更新日期: 2023-09-27 18:27:43

我想知道有没有一种方法可以使用Linq-to-XML从XML字符串中获取对象数组。。。我习惯于使用XPath来做我所有的脏活,而XPath似乎很直观。但是,我一直听说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对象的一个实例(PatientClass的列表……我在列表中将它们命名为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),
                                               }

它目前正在返回null值,但我正在处理。。。但是我怎么能得到这些病人的名单呢。我可能需要一个超级查询来处理物品列表,而子查询来获得患者信息,对吗?

所以像这样的事情。。。

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

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

的基础

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

我们在工作中使用了手工滚动的linq到xml,所以我要试一试。

需要注意的是,所有EMPIID元素都有一个值=",这将在调用int.Parse时导致FormatExceptionXAttribute上有一系列显式的强制转换运算符,它们可以很好地让您使用常见的.net类型。

以下是我可能为您需要进行的解析编写的代码(在不使用XPath的情况下保持纯xnode解析风格):

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到xml无缝过渡到使用linq到对象,这使得linq非常引人注目。不管怎样,来LINQ游泳池吧,水很好。:)