【问题标题】:LINQ - XML select node genericLINQ - XML 选择节点通用
【发布时间】:2013-11-07 02:13:43
【问题描述】:

我正在尝试剥离我的 XML 并只保留我需要的节点

输入 XML 是

 <Employees>
  <Employee>
    <EmpId>1</EmpId>
    <Name>Sam</Name>
    <Sex>Male</Sex>
    <Address>
      <Country>USA</Country>
      <Zip>95220</Zip>
    </Address>
    <Address2>
      <Country>UK</Country>
      <Zip>E157JQ</Zip>
    </Address2>
  </Employee>
  <Employee>
    <EmpId>2</EmpId>
    <Name>Lucy</Name>
    <Sex>Female</Sex>
    <Address>
      <Country>USA</Country>
      <Zip>95220</Zip>
    </Address>
    <Address2>
      <Country>UK</Country>
      <Zip>E184JQ</Zip>
    </Address2>
  </Employee>
</Employees>

我的代码如下。

private void button1_Click(object sender, EventArgs e)
    {
        Stream s = openFileDialog1.OpenFile();
        var xDoc = XDocument.Load(s);
        string keep = "EmpId,Sex,Address,Zip,Address2,Country"; \\ I can change this format
        string desStr = "Employee";
            string[] strArr = keep.Split(',');

            var nodesToDelete = xDoc.Root.Descendants(desStr)
                .SelectMany(el => el.Descendants()
                                  .Where(a => !strArr.Contains(a.Name.ToString())));

            foreach (var node in nodesToDelete.ToList())
                node.Remove();

            richTextBox1.Text = xDoc.ToString();
    }

我从上面得到的输出是

<Employees>
      <Employee>
        <EmpId>1</EmpId>
        <Sex>Male</Sex>
        <Address>
          <Country>USA</Country>
          <Zip>95220</Zip>
        </Address>
        <Address2>
          <Country>UK</Country>
          <Zip>E157JQ</Zip>
        </Address2>
      </Employee>
      <Employee>
        <EmpId>2</EmpId>
        <Sex>Female</Sex>
        <Address>
          <Country>USA</Country>
          <Zip>95220</Zip>
        </Address>
        <Address2>
          <Country>UK</Country>
          <Zip>E184JQ</Zip>
        </Address2>
      </Employee>
    </Employees>

我需要的输出是

<Employees>
  <Employee>
    <EmpId>1</EmpId>
    <Sex>Male</Sex>
    <Address>
     <Zip>95220</Zip>
    </Address>
    <Address2>
      <Country>UK</Country>
    </Address2>
  </Employee>
  <Employee>
    <EmpId>2</EmpId>
    <Sex>Female</Sex>
    <Address>
      <Zip>95220</Zip>
    </Address>
    <Address2>
      <Country>UK</Country>
    </Address2>
  </Employee>
</Employees>

我如何查询 Address\Zip 和 Address2\Country 我需要它是通用的(因此字符串保持可以更改)所以我不能硬编码节点名称。

谢谢

【问题讨论】:

  • 你到底想做什么?你想让 XML 看起来像 Address 节点下总是只有 Zip 并且只有 Country - 在 Address2下>?
  • 我试图让它输入驱动,所以如果字符串 keep = "EmpId,Sex,Address,Zip,Address2,Country"; \\ 我可以更改这种格式,所以如果 Address\Zip 那么我的 XML 应该有
    95220
    如果它的 Address2\Country 那么我的 XML 应该是 UK 地址2>
  • 您需要输入保持为字符串格式吗?因为你要做的事情需要一个树状的输入定义,解析这样的字符串会很麻烦。
  • 不一定是字符串格式。它将作为字符串传递,但我可以将其转换为任何格式/对象。
  • 问题是您是通过 UI 接收来自最终用户还是来自某些配置的输入。在配置的情况下,最好使用最终XML格式的XSD;在用户输入的情况下,您将不得不提供方便的 UI 或发明另一种格式,允许您指定节点的层次结构。

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


【解决方案1】:

我的方式:

string keep = "Employees,Employee,EmpId,Sex,Address2,Address,Address.Zip,Address2.Country"; // I can change this format
string[] strArr = keep.Split(',');

    foreach (var node in xDoc.Descendants().ToArray())
    {
        var path = Path(node);
        if (!strArr.Any(path.EndsWith))
        {
            node.Remove();
        }
    }

    var results = xDoc.ToString();
}

private static string Path(XElement x)
{
    if (x.Parent != null)
    {
        return Path(x.Parent) + "." + x.Name;
    }

    return x.Name.ToString();
}

【讨论】:

    【解决方案2】:

    好吧,这取决于您希望这是通用的程度。 这是保持当前代码结构的一种有点骇人听闻的方式。

    string keep = @"EmpId,Sex,Address,Address\Zip,Address2,Address2\Country";
    string desStr = "Employee";
    string[] strArr = keep.Split(',');
    
    var nodesToDelete = xDoc.Root.Descendants(desStr)
                    .SelectMany(el => el.Descendants()
                                      .Where(a => 
                                        {
                                            if (a.Parent.Name == desStr)
                                            {
                                                return !strArr.Contains(a.Name.ToString());
                                            }
                                            else
                                            {
                                                return !strArr.Contains(a.Parent.Name + @"\" + a.Name);
                                            }
    
                                        }));
    
    foreach (var node in nodesToDelete.ToList())
          node.Remove();
    

    正确的方法是保留所有要保留的节点的完整路径。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-18
      • 2013-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多