2017-02-04 140 views
0

编辑根据@Gert Arnold的建议,我决定编辑并更彻底地设置我的问题的格式。选择通过Linq在C#中选择两个子节点的节点

我一直在试图通过Linq选择通过id和值条件的节点。在我的情况下,我需要在SeriesKey节点中具有具有两个特定value属性的节点的series

这里是我的XML字符串(仅供参考,如果你发现任何标记错误,他们可能是由于我的压痕错误,原始文件是XML有效

<message:GenericData xmlns:footer="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message/footer" 
        xmlns:generic="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" 
        xmlns:message="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message" 
        xmlns:common="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
<message:DataSet> 
    <generic:Series> 
     <generic:SeriesKey> 
      <generic:Value id="GEO" value="124"/> 
      <generic:Value id="PRODUCT" value="4400"/> 
      <generic:Value id="FIN" value="03"/> 
      <generic:Value id="ENERGY_UNITS" value="WSR"/> 
     </generic:SeriesKey> 
     <generic:Obs> 
      <generic:ObsDimension id="TIME_PERIOD" value="1999"/> 
      <generic:ObsValue value="0"/> 
      <generic:Attributes> 
       <generic:Value id="UNIT_SUFFIX" value="R"/> 
      </generic:Attributes> 
     </generic:Obs> 
     <generic:Obs> 
      <generic:ObsDimension id="TIME_PERIOD" value="2000"/> 
      <generic:ObsValue value="0"/> 
     <generic:Attributes> 
     <generic:Value id="UNIT_SUFFIX" value="R"/> 
     </generic:Attributes> 
     </generic:Obs> 
</generic:Series> 
<generic:Series> 
    <generic:SeriesKey> 
     <generic:Value id="GEO" value="124"/> 
     <generic:Value id="PRODUCT" value="4100"/> 
     <generic:Value id="FIN" value="03"/> 
     <generic:Value id="ENERGY_UNITS" value="WSR"/> 
    </generic:SeriesKey> 
    <generic:Obs> 
     <generic:ObsDimension id="TIME_PERIOD" value="1999"/> 
     <generic:ObsValue value="8246"/> 
     <generic:Attributes> 
      <generic:Value id="UNIT_SUFFIX" value="R"/> 
     </generic:Attributes> 
    </generic:Obs> 
    <generic:Obs> 
     <generic:ObsDimension id="TIME_PERIOD" value="2000"/> 
     <generic:ObsValue value="40733"/> 
     <generic:Attributes> 
      <generic:Value id="UNIT_SUFFIX" value="R"/> 
     </generic:Attributes> 
    </generic:Obs> 
    </generic:Series> 
    <generic:Series> 
     <generic:SeriesKey> 
      <generic:Value id="GEO" value="124"/> 
      <generic:Value id="PRODUCT" value="4200"/> 
      <generic:Value id="FIN" value="03"/> 
      <generic:Value id="ENERGY_UNITS" value="WSR"/> 
     </generic:SeriesKey> 
     <generic:Obs> 
      <generic:ObsDimension id="TIME_PERIOD" value="1999"/> 
      <generic:ObsValue value="279"/> 
      <generic:Attributes> 
       <generic:Value id="UNIT_SUFFIX" value="R"/> 
      </generic:Attributes> 
     </generic:Obs> 
     <generic:Obs> 
      <generic:ObsDimension id="TIME_PERIOD" value="2000"/> 
      <generic:ObsValue value="324"/> 
      <generic:Attributes> 
       <generic:Value id="UNIT_SUFFIX" value="R"/> 
      </generic:Attributes> 
     </generic:Obs> 
    </generic:Series> 
</message:DataSet> 
</message:GenericData> 

我试着去查询方式,只是请在where声明中看到,使用逻辑运算符创建一系列步骤。我已经提到了这个方法。此时它接受一个xml字符串(上面的一个)和两个过滤条件,即EnergyProduct来过滤PRODUCT属性和EconSector以过滤FIN属性。

public IEnumerable<XElement> DataSetFilter(string XmlString, string EnergyProduct, string EconSector) 
    { 
     XDocument sdmx_response = XDocument.Parse(XmlString); 
     XNamespace message = "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message"; 
     XNamespace generic = "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic"; 
     IEnumerable<XElement> DataSet = sdmx_response.Root.Elements(message + "DataSet"); 
     IEnumerable<XElement> Series = from series in DataSet.Elements(generic + "Series") 
        from serieskey in series.Elements(generic + "SeriesKey") 
        from value in serieskey.Elements(generic + "Value") 
        where 
        (
         (string)value.Attribute("id") == "PRODUCT" && (string)value.Attribute("value") == EnergyProduct 
        ) || 
        (
         (string)value.Attribute("id") == "FIN" && (string)value.Attribute("value") == EconSector 
        ) 
        select serieskey; 
     IEnumerable <XElement> observationsSet = from observations in Series.Elements(generic + "Obs").Elements(generic + "ObsValue") select observations; 
     return observationsSet; 
    } 

的问题是,它抓住了每个属性的所有数据,例如匹配PRODUCT代码“4400”和FIN码“03”和我在寻找什么为的那些仅仅是包含节点具有这些确切值的子节点,都在相同的SeriesKey中。我正在考虑创建一个匿名对象,该对象包含我想要的xml元素,但是我遇到了错误,我仍然对如何正确实现它感到困惑。 谢谢你的帮助!

+1

您的XML无效。如果您将其上传到http://www.xmlvalidation.com,您将看到错误“16:\t 7”\t“元素类型”系列“必须由匹配的结束标记”“终止。”您是否可以共享有效的XML ? – dbc

+0

谢谢。看起来我在粘贴代码时犯了一个错误。在我的情况下,XML验证不是问题。 – Cristiansen

+0

如果您想要满足* both *条件的子节点,不完全确定为什么在'where'子句中使用'||'。 – Tomer

回答

2

尝试以下操作:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Xml; 
using System.Xml.Linq; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     const string FILENAME = @"c:\temp\test.xml"; 
     static void Main(string[] args) 
     { 
      XDocument sdmx_response = XDocument.Load(FILENAME); 
      XNamespace message = sdmx_response.Root.GetDefaultNamespace(); 
      XNamespace generic = sdmx_response.Root.GetDefaultNamespace(); 

      IEnumerable<XElement> DataSet = sdmx_response.Root.Elements(message + "DataSet"); 
      IEnumerable<XElement> Series = DataSet.Elements(generic + "Series").Select(series => new XElement("Series", new object[] { 
       new XElement("SeriesKey", 
        series.Elements(generic + "SeriesKey").Elements("Value").Where(value =>((string)value.Attribute("id") == "PRODUCT" && (string)value.Attribute("value") == "Lumber") || ((string)value.Attribute("id") == "FIN" && (string)value.Attribute("export") == "Lumber")) 
        ), 
       series.Elements(generic + "Obs") 
      })).ToList(); 

     } 
    } 



} 
+3

虽然这段代码片段可能回答OP的问题,但如果您添加解释,答案会更有用。请注意,仅有代码的答案往往会被标记为质量审查,这可能会导致它们被删除。 –

0

我已经upvoted和选择jdweng的回答是最合适的解决方案。这是我的代码。

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     const string FILENAME = @"c:\temp\test.xml"; 
     static void Main(string[] args) 
     { 
      IEnumerable<XElement> NormalizedDataSet = NormalizeGeneric(FILENAME); 
      foreach (XElement Series in NormalizedDataSet) 
      { 
       Console.WriteLine(Series); 
      } 
     } 

     public IEnumerable<XElement> NormalizeGeneric(string XmlString) 
     { 
      XDocument xml_response = XDocument.Parse(XmlString); 
      XNamespace message = "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message"; 
      XNamespace generic = "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic"; 
      XElement SeriesSet = xml_response.Root; 
      IEnumerable<XElement> SeriesObject = seriesSet.Elements(message + "DataSet") 
                  .Elements(generic + "Series") 
                  .Select(series => new XElement("Series", new object[] 
      { 
       new XElement("Metadata", 
          series.Elements(generic + "SeriesKey") 
            .Elements(generic + "Value") 
            .Select(value => new XElement((string)value.Attribute("id"), new XAttribute("value", (string)value.Attribute("value"))))), 
       new XElement("Data", 
          series.Elements(generic + "Obs") 
            .Select(observations => new XElement("Observation", new XAttribute((string)observations.Element(generic + "ObsDimension") 
                                 .Attribute("id"), (string)observations.Element(generic + "ObsDimension").Attribute("value")), new XAttribute("value", (string)observations.Element(generic + "ObsValue").Attribute("value")), new XElement("Attributes", observations.Elements(generic + "Attributes").Elements(generic + "Value").Select(attributes => new XElement((string)attributes.Attribute("id"), new XAttribute("value", (string)attributes.Attribute("value")))))))) 
      })).ToArray(); 
      return SeriesObject; 
     } 
    } 
} 

我的代码和jdweng的区别在于,我还添加了包含实际数字的文件的数据部分。数据集的“规范化”是不可避免的,因此更容易处理该值并过滤必要的节点。谢谢,并对迟到的回应和缩进道歉。