从子元素列表中只取一个中间元素

时间:2010-10-05 00:28:27

标签: c# xml linq-to-xml parent

问候,

我有一个问题和一个截止日期(我只知道在这个困境中有一些知道的点头)

背景:我需要以分层的方式存储数据元素。简而言之,我的xml是一组规则,用于指示图形绘制引擎根据严格的父子关系在图表上绘制多个系列。即一系列'可以'用于其源数据,其父系列的结果系列等等......或者它可能不会。 我认为这种方法的强大之处在于,如果我需要删除父系列,删除父xml节点非常简单,所有依赖/子系列/节点也将被删除......魔法就像魅力一样。我几乎是LINQ转换器。

  

我有一张图片来说明   输出,但显然我太多了   允许发布图像的新手   ..所以想象一下画出4条波浪线   基于的图表3   以前和一个只是生活大   在它寂寞...愚蠢的图像张贴   但是,我要做什么:-( ..

表示此关系的XML如下所示。

<Chart>
  <Chart_Config AxisClear="False">
    <Color>white</Color>
    <Panels Drawn="True">3</Panels>
    <SeriesCount>5</SeriesCount>
    <ChartStyle>Candle</ChartStyle>
    <DataMode>Daily</DataMode>
  </Chart_Config>
  <Series ID="0" Drawn="True">
    <Name>0.AAC</Name>
    <StockCode>AAC</StockCode>
    <TID>0</TID>
    <IndID>-1</IndID>
    <PID>0</PID>
    <iType>0</iType>
    <Parent>0</Parent>
    <Series ID="1" Drawn="True">
      <Name>1.SMA</Name>
      <StockCode>AAC</StockCode>
      <TID>0.AAC</TID>
      <IndID>0</IndID>
      <PID>2</PID>
      <iType>1</iType>
      <Parent>0.AAC</Parent>
      <Parameters>
        <Param Name="Period" Type="Integer" Min="1" Max="999">10</Param>
        <Param Name="Color" Type="Color" Min="0" Max="0">0, 0, 192</Param>
        <Param Name="Transparency" Type="Integer" Min="0" Max="100">0</Param>
        <Param Name="Width" Type="Integer" Min="1" Max="99">2</Param>
      </Parameters>
      <Series ID="2" Drawn="True">
        <Name>2.SMA</Name>
        <StockCode>AAC</StockCode>
        <TID>1.SMA</TID>
        <IndID>0</IndID>
        <PID>0</PID>
        <iType>1</iType>
        <Parent>1.SMA</Parent>
        <Parameters>
          <Param Name="Period" Type="Integer" Min="1" Max="999">20</Param>
          <Param Name="Color" Type="Color" Min="0" Max="0">0, 192, 0</Param>
          <Param Name="Transparency" Type="Integer" Min="0" Max="100">0</Param>
          <Param Name="Width" Type="Integer" Min="1" Max="99">2</Param>
        </Parameters>
        <Series ID="3" Drawn="True">
          <Name>3.SMA</Name>
          <StockCode>AAC</StockCode>
          <TID>2.SMA</TID>
          <IndID>0</IndID>
          <PID>0</PID>
          <iType>1</iType>
          <Parent>2.SMA</Parent>
          <Parameters>
            <Param Name="Period" Type="Integer" Min="1" Max="999">30</Param>
            <Param Name="Color" Type="Color" Min="0" Max="0">192, 0, 192</Param>
            <Param Name="Transparency" Type="Integer" Min="0" Max="100">0</Param>
            <Param Name="Width" Type="Integer" Min="1" Max="99">2</Param>
          </Parameters>
        </Series>
      </Series>
    </Series>
    <Series ID="4" Drawn="True">
      <Name>4.SMA</Name>
      <StockCode>AAC</StockCode>
      <TID>0.AAC</TID>
      <IndID>0</IndID>
      <PID>3</PID>
      <iType>1</iType>
      <Parent>0.AAC</Parent>
      <Parameters>
        <Param Name="Period" Type="Integer" Min="1" Max="999">40</Param>
        <Param Name="Color" Type="Color" Min="0" Max="0">192, 0, 0</Param>
        <Param Name="Transparency" Type="Integer" Min="0" Max="100">0</Param>
        <Param Name="Width" Type="Integer" Min="1" Max="99">2</Param>
      </Parameters>
    </Series>
  </Series>
</Chart>

我发现的问题是,有时我需要更改我可以定位的XML的参数部分中的值并检索所需的节点..但该节点也包含任何子节点。 例如,如果我检索名称为“Series”且属性ID为“2”的XElement,我也会获得ID = 3的Series条目。

问题A部分:(最后我听到了尖叫声)....使用这个现有的结构,我如何只检索我的一个系列元素而不是它的子元素,这样我就可以单独更新它的参数。

和B部分:这是格式化我的XML以实现这个非常有用的父子关系的正确方法......

干杯斯纳克......

P.S。如果您有足够的回答,我是否可以打扰您对提议的解决方案如何工作的最小描述....我第一次使用LINQ to XML而不是像我希望的那样直截了当我仍然认为关系数据库。

在阅读了那些帖子后,我讽刺了我的极端蛮力方法(我们都不会错过那些优雅的设计只是营销高管谈到要出售的东西)......任何方式。

           /// <summary>
           /// Updates Parameters in conjunction with the Modify paramteters Dialog
           /// </summary>
           /// <param name="Message"></param>
            private void UpdateIndicatorParameters(IndicatorParamterUpdate Message)
            {
                IEnumerable<XElement> result;
                /// Find the series
                if (Message.isIndicator)
                {
                    result = from e in ChartObj.ChartSeriesXMLRules.Descendants()
                             where (e.Name.ToString() == "Series" && e.Attribute("ID").Value == Message.IndicatorID)
                             select e;
                }
                else
                {
                  // not relevant 
                }



                var NodeOfInterest = result;

                /// Find Parameter section
                foreach (var el in result)
                {
                    NodeOfInterest = el.Elements("Parameters");
                }

/// Find individual paramters                
var result2 = from e in NodeOfInterest.Descendants()
                              where e.Name.ToString() == "Param"
                              select e;

/// Update required paramter                
foreach (var el in result2)
                {
                    if (el.Attribute("Name").Value == Message.ParameterName)
                    {
                        el.Value = Message.Value;

                    }
                }
            }

2 个答案:

答案 0 :(得分:1)

如果你使用XPath,它很简单:

/Chart//Series[@ID='4']/Parameters/Param[@Name='Color']

只会找到特定Parameters元素的直接子元素的Series元素,并且只会找到紧跟在该Param元素下的指定Parameters元素。

它会在文档中的任何位置查找Series元素,如果你有一个大文档要搜索并且你正在做很多事情,那么效率会有点低效;在这种情况下,建立地图可能是值得的,例如:

var seriesMap = rootXElement
   .SelectXPathElements("//Series")
   .ToDictionary(x => x.Attribute("ID").Value);

然后你可以得到一个给定的Param元素:

string pattern = string.Format("Parameters/Param[@Name='{0}']", name);
seriesMap[id].SelectXPathElement(pattern);

答案 1 :(得分:0)

如果你把它看作xml,你 总是会有后代 - 否则你就是在破坏xml。但只要你只看一下应该没事的直系孩子的Parameters。如果将它映射到对象模型,您将采用类似的方法,但是您将忽略Items(或者您称之为子成员的任何内容)。

没有“正确的方法”,但是,应该就足够了。在某些情况下,可能想要考虑 flat 模型,但层次结构似乎适合这些数据。通过“平面”模型,我的意思是你只需要:

 <Series ID="1" ...>...</Series>
 <Series ID="2" ...>...</Series>
 <Series ID="3" ...>...</Series>
 <Series ID="4" ...>...</Series>

请注意,您仍然可以使用现有的<Parent>值来推断关联,而不将其作为实际布局的一部分。我不是说要么是正确的 - 只是两者都是合法的。实际上,目前你有冗余,你通过结构通过值来表达这种关系;这是罚款,直到你犯了一个错误并更新一个而不是另一个。