【问题标题】:Linq-to-XML for retrieving all child nodes用于检索所有子节点的 Linq-to-XML
【发布时间】:2013-08-24 10:21:09
【问题描述】:

我有以下 XML

 <?xml version="1.0" encoding="utf-8"?>
 <ErrorServer>
   <ClientIP>
     <AllowAll>false</AllowAll>
     <Client_127_0_0_1>true</Client_127_0_0_1>
   </ClientIP>
   <Users>
     <Admin>
       <Password>passw0r!d</Password>
       <NextError>83</NextError>
       <Active>true</Active>
     </Admin>
     <JimBob>
       <Password>passw0r!d</Password>
       <NextError>83</NextError>
       <Active>true</Active>
     </JimBob>
   </Users>
 </ErrorServer>

在 c# 中使用 linq 我正在尝试使用以下代码获取所有用户名(上例中的 Admin 和 JimBob)

    List<string> Result = new List<string>();

    XDocument xdoc = XDocument.Load("ErrorServerConfig.xml");

    //Run query
    var lv1s = from lv1 in xdoc.Descendants("ErrorServer")
               select new
               {
                   Children = lv1.Elements("Users")
               };

    //Loop through results
    foreach (var lv1 in lv1s)
    {
        foreach (var lv2 in lv1.Children)
            Result.Add(lv2.Name.ToString());
    }

    return (Result);

这不起作用,因为它只在结果中返回“用户”。

我是 linq 新手,谁能告诉我正确的做法吗?

【问题讨论】:

  • 更典型的做法是将 xml 元素名称视为项目的通用类,而将实际名称定义为属性或子元素的字符数据,例如: jimbob - 如果您这样做,您将遵循更典型的模式,并且 XDocument Api 似乎更自然地使用

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


【解决方案1】:
var result = xdoc.Descendants("Users")
                 .First()
                 .Elements()
                 .Select(e=>e.Name);

【讨论】:

  • @Anirudh 这就是它的逻辑。如果您删除First(),查询将完全不同。
  • Descendants().First() 的效率会略低于Root.Element()
  • @MarcinJuraszek 谢谢,至少在这种情况下是这样。如果Usersnode tree 中缩进更多,Descendants 会更简洁。
  • @KingKing 是的,但效率和可预测性仍然较低 :)
【解决方案2】:

谢谢大家,我已经按照 Aaron Anodide 的建议更改了我的 xml,这本来就是应该的(我的错)。

Xml 现在看起来像:

<ErrorServer>
  <Users>
    <User>
      <Username>Admin</Username>
      <Password>passw0r!d</Password>
      <NextError>83</NextError>
      <Active>true</Active>
    </User>
  </Users>
</ErrorServer>

并使用以下代码

1 选择所有用户名

        XDocument xdoc = XDocument.Load("ErrorServerConfig.xml");

        //Run query
        var result = from e in xdoc.Descendants("Users").Elements()
             select (string)e.Element("Username");

        //Loop through results
        foreach (string user in result)
        {
            Result += String.Format("{0}\r\n", user);
        }

2 获取密码

    XDocument xdoc = XDocument.Load("ErrorServerConfig.xml");

    //Run query
    var result = (from e in xdoc.Descendants("Users").Elements()
                 where (string)e.Element("Username") == userName
                 select e).Descendants("Password").First().Value;

两者都比我之前的 xml 代码优雅得多,感谢您的帮助和指点。

【讨论】:

  • 既然您已经在运行一个查询,为什么不使用 sape 查询获取密码信息并将其存储起来呢?
【解决方案3】:
var res =  XDocument.Load("yourpath")
    .Descendants("Users").Elements()
    .Select(xe => xe.Name.LocalName);

如果您想返回IEnumerable&lt;XName&gt;,请使用.Name,如果您想返回IEnumerable&lt;string&gt;,请使用Name.LocalName。这只是我的意见,但在您的课堂上,我会将 xdoc 设置为属性。

新更新


这实际上在今天(2014 年 8 月 7 日)获得了投票,这促使我查看它并批评我自己的工作。然后我意识到这完全是废话......

  1. 正如 Aaron Anodide 在 OP 问题中所评论的那样,Xml 架构完全错误,这就是使这项任务如此艰巨的原因......

  2. 启用蹩脚代码让我感到羞耻。

  3. 为发布蹩脚的代码感到羞耻

它应该实际实现的方式。呸呸呸呸


XML:

<?xml version="1.0" encoding="utf-8" ?>
<ErrorServer>
  <ClientIP>
    <AllowAll>false</AllowAll>
    <Address>127.0.0.1</Address>
  </ClientIP>
  <Users>
    <User>
      <Username>Admin</Username>
      <Password>passw0r!d</Password>
      <NextError>83</NextError>
      <Active>true</Active>
    </User>
    <User>
      <Username>JimBob</Username>
      <Password>passw0r!d</Password>
      <NextError>83</NextError>
      <Active>true</Active>
    </User>
  </Users>
</ErrorServer>

类:

#region Referencing

using System;
using System.IO;
using System.Linq;
using System.Xml.Serialization;

#endregion

namespace Stack
{
    public class Program
    {
        public Program()
        {
            ErrorServer = ErrorServer.Deserialize( "path" );
        }

        public ErrorServer ErrorServer { get; set; }

        // This way you dont actually have to deal with LINQ and XML.
        // It's just as easy to create a few classes to hold your data, so you can use xml serialization.
        public User GetUserInfoByName( string name )
        {
            return
                ErrorServer.Users.FirstOrDefault(
                    user => user.Username.Equals( name, StringComparison.CurrentCultureIgnoreCase ) );
        }
    }

    [Serializable]
    public class ErrorServer
    {
        public ClientIP ClientIP { get; set; }

        [XmlArrayItem( "User" )]
        public User[] Users { get; set; }

        public static ErrorServer Deserialize( string path )
        {
            using (var stream = new FileStream( path, FileMode.Open ))
                return new XmlSerializer( typeof (ErrorServer) ).Deserialize( stream ) as ErrorServer;
        }
    }

    [Serializable]
    public class ClientIP
    {
        public bool AllowAll { get; set; }

        public string Address { get; set; }
    }

    [Serializable]
    public class User
    {
        public string Username { get; set; }

        public string Password { get; set; }

        public double NextError { get; set; }

        public bool Active { get; set; }
    }
}

所以看在上帝的份上,请不要在线以下使用任何东西。


更新

抱歉,拖了这么久。这是我为你准备的一个小班。

using System;
using System.Linq;
using System.Xml.Linq;

namespace StackTesting
{
    class Program
    {
        public class User
        {
         public string Username { get; set; }
         public string Pass { get; set; }
         public double Error { get; set; }
         public bool Active { get; set; }

          public User() { }
        }

        Public XDocument xDoc { get; set; }

        static void Main(string[] args)
        {
         xDoc = XDocument.Load(@"C:\Users\Trae\Documents\visual studio 2012\Projects\StackTesting\StackTesting\XMLFile1.xml");
          var user = (User) GetUserInfo("Admin");
        }

        public static User GetUserInfo(string UserName)
        {
          return xDoc.Root.Elements("Users").Elements()
            .Where(xe => xe.Element(XName.Get("Username")).Value == UserName)
            .Select(xe =>
              new User
              {
                Username = xe.Element(XName.Get("Username")).Value,
                Pass = xe.Element(XName.Get("Password")).Value,
                Error = double.Parse(xe.Element(XName.Get("NextError")).Value),
                Active = bool.Parse(xe.Element(XName.Get("Active")).Value)
              }).ToArray()[0];
        }
    }
}

【讨论】:

    【解决方案4】:
    var result = xdoc.Root
                     .Element("Users")
                     .Elements()
                     .Select(x => x.Name);
    

    【讨论】:

    • 不需要 Root 属性,因为您已经在根目录下
    • @Anirudh 这是因为我们使用的是 XDocument。测试一下。
    • @Anirudh Root IS 当您的查询从 XDocument 实例开始时是必需的。
    • @MarcinJuraszek 甚至 Descendants 都不能用吗?
    • @KingKing 可以,因为往文档树的深处看,所以往上一层启动是没有问题的。
    猜你喜欢
    • 1970-01-01
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多