【问题标题】:Reading an XML file and create a list of objects that match the XML content读取 XML 文件并创建与 XML 内容匹配的对象列表
【发布时间】:2014-09-05 12:07:17
【问题描述】:

我有一个 XML 文件,其中包含有关特定计算机上安装了哪些组件以及属于每个组件的配置文件的一些信息。

文件的一个开发示例如下所示:

<?xml version="1.0" encoding="utf-8"?>
<ConfigMappings>

  <Host Name="Host1">
    <Component Name="AlarmManager" Version="1.0.1.0" Enabled="True">
      <Config Name="AlarmManager.Configuration.xml" Version="1.0.0.0" SubFolder=""/>
      <Config Name="faultMapConfig.xml" Version="1.0.0.0" SubFolder=""/>
      <Config Name="LoadsCalculatorConfig.xml" Version="1.0.0.0" SubFolder="Advisory"/>
    </Component>
  </Host>

<Host Name="Host2">
    <Component Name="AdvisoryManager" Version="1.0.1.0" Enabled="True">
      <Config Name="DeviceManager.Configuration.xml" Version="1.0.0.1" SubFolder=""/>
      <Config Name="BasicData.xml" Version="1.0.0.2" SubFolder="Advisory"/>
      <Config Name="BasicData.xsd" Version="1.0.0.3" SubFolder="Advisory"/>
      <Config Name="Utilization.xml" Version="1.0.0.4" SubFolder=""/>
      <Config Name="Utilization.xsd" Version="1.0.0.4" SubFolder=""/>
      <Config Name="faultMapConfig.xsd" Version="1.0.0.4" SubFolder=""/>
      <Config Name="faultMapConfig.xml" Version="1.0.0.4" SubFolder=""/>
    </Component>
    <Component Name="w32time" Version="1.0.1.0" Enabled="True">
       <Config Name="DeviceManager.Configuration.xml" Version="1.0.0.1" SubFolder=""/>
    </Component>
  </Host>    
</ConfigMappings>

我想做的是读取这个 XML 文件,并在其中找到与我的计算机主机名匹配的 Host 元素。我想创建一个包含名称、版本和启用属性的组件列表 + 其配置列表(名称、版本、子文件夹)。

我自己做到了这一点:

var xdoc = XDocument.Load(dlg.FileName);
var components =
    from c in xdoc.Descendants("Component")
    where
        String.Equals(c.Parent.Attribute("Name").Value, Environment.MachineName, StringComparison.CurrentCultureIgnoreCase)
    select new
    {
        Name = c.Attribute("Name").Value,
        Enabled = c.Attribute("Enabled").Value,
        version = c.Attribute("Version").Value,
    };

ComponentList.Clear();
foreach (var component in components)
{
    bool componentEnable = component != null && component.Enabled == "True";
    ComponentList.Add(new Component(component.Name, componentEnable, component.version));
}

foreach (var component in ComponentList)
{
    var configs =
    from c in xdoc.Descendants("Config")
    where 
    c.Parent.Parent.Name == "Host" &&
    String.Equals(c.Parent.Parent.Attribute("Name").Value, Environment.MachineName, StringComparison.CurrentCultureIgnoreCase) &&
    String.Equals(c.Parent.Attribute("Name").Value, component.Name, StringComparison.CurrentCultureIgnoreCase)
    select new
    {
        Name = c.Attribute("Name").Value,
        version = c.Attribute("Version").Value,
        SubFolder = c.Attribute("SubFolder").Value,
    };

    foreach (var config in configs)
    {
        component.Configurations.Add(new Config(config.Name, config.version, config.SubFolder));
    }
}

这里的 ComponentList 是一个组件列表。而 Component 是一个包含其属性(Name、Version 和 Enabled)+ 配置对象列表的类。

现在可以了。但这似乎是一种非常混乱和凌乱的方式,我需要一些帮助来改进这一点。

【问题讨论】:

    标签: c# xml linq parsing


    【解决方案1】:
    namespace ConsoleApplication3
    {
        using System;
        using System.Collections.Generic;
        using System.Diagnostics;
        using System.Linq;
        using System.Net;
        using System.Text;
        using System.Threading.Tasks;
        using System.Xml.Linq;
    
     public class Config
            {
                public string Name { get; set; }
                public string Version { get; set; }
                public string SubFolder { get; set; }
            }
    
        public class Component
        {
            public List<Config> SystemConfig;
            public string Name { get; set; }
            public string Version { get; set; }
            public string Enabled { get; set; }
            public Component()
            {
    
                this.SystemConfig = new List<Config>();
            }
        }
    
        public class Program
        {
    
            public static void Main()
            {
                XDocument xmlDocument = XDocument.Load(@"c:\Visual Studio\Projects\ConsoleApplication3\ConsoleApplication3\config.xml");
                var item = (from template in xmlDocument.Descendants("Component")
                            where template.Parent.Attribute("Name").Value.Equals("Host1", StringComparison.CurrentCultureIgnoreCase)
                            select new Component()
                            {
                                Version = template.Attribute("Version").Value,
                                Name = template.Attribute("Name").Value,
                                Enabled = template.Attribute("Enabled").Value,
                                SystemConfig = (
                                   from t in template.Elements("Config")
                                   select new Config()
                                   {
                                       Name = t.Attribute("Name").Value,
                                       SubFolder = t.Attribute("SubFolder").Value,
                                       Version = t.Attribute("Version").Value
                                   }
                                   ).ToList()
                            }
                                     ).FirstOrDefault();
            }
        }
    }
    

    请随意删除硬编码值。上面的代码会给你第一个匹配主机名的对象。

    【讨论】:

    • 在查询中删除 .FirstOrDefault() 并执行 Item.ToList() 这得到了我想要的结果:) 方法与我自己尝试的相似,但代码很多更清洁,切中要害。感谢您的帮助!
    • 很高兴能帮到你。
    【解决方案2】:

    不确定这是否有帮助,但为了阅读 XML,我通常使用如下代码:

    XmlDocument doc = new XmlDocument();
    doc.Load(pathOfYourXmlDocument);
    

    然后你可以这样做:

    XmlElement root= doc["ConfigMappings"];
    

    然后你可以像这样选择其他节点:

    XmlNode host= root.SelectSingleNode("Host1"); 
    XmlNode component = host.SelectSingleNode("Component");
    

    最后创建一个组件的所有子节点的列表,如下所示:

    XmlNodeList configList = component.SelectNodes("Config");
    

    现在您可以遍历 XmlNodeList 并获取内部文本、属性或您需要的任何内容。不确定这是否对您有帮助,并且显然没有正确或错误的方法,只是不同的解决方案,有些可能很漂亮,有些可能有效,有些可能两者兼而有之。这是我读取 Xml 文件并使用它们的方式,但还有更多可能性。

    【讨论】:

      【解决方案3】:

      如果你有 POCO,那么你可以写(注意字符串扩展为 Deserialize xml (source)):

      ConfigMappings item;
      if(File.ReadAllText(@"d:\in.csv").Deserialize(out item)){
        //item is loaded
      }
      

      获取 POCO copy & paste special - as xml classes(最低 VS2012.2 RC)。

      [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
      [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
      public partial class ConfigMappings {
          [System.Xml.Serialization.XmlElementAttribute("Host")]
          public ConfigMappingsHost[] Host { get; set; }
      }
      [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
      public partial class ConfigMappingsHost {
          [System.Xml.Serialization.XmlElementAttribute("Component")]
          public ConfigMappingsHostComponent[] Component { get; set; }
          [System.Xml.Serialization.XmlAttributeAttribute()]
          public string Name { get; set; }
      }
      [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
      public partial class ConfigMappingsHostComponent {
          [System.Xml.Serialization.XmlElementAttribute("Config")]
          public ConfigMappingsHostComponentConfig[] Config { get; set; }
          [System.Xml.Serialization.XmlAttributeAttribute()]
          public string Name { get; set; }
          [System.Xml.Serialization.XmlAttributeAttribute()]
          public string Version { get; set; }
          [System.Xml.Serialization.XmlAttributeAttribute()]
          public string Enabled { get; set; }
      }
      [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
      public partial class ConfigMappingsHostComponentConfig {
          [System.Xml.Serialization.XmlAttributeAttribute()]
          public string Name { get; set; }
          [System.Xml.Serialization.XmlAttributeAttribute()]
          public string Version { get; set; }
          [System.Xml.Serialization.XmlAttributeAttribute()]
          public string SubFolder { get; set; }
      }
      

      【讨论】:

      • 这适用于读取 XML 的全部内容。但我只关注那些特定于我的部分。我只想要 等于我的计算机主机名的组件和配置。是否可以先对 XML 进行简单的过滤,以找到 适合我的计算机主机名的元素。然后将属于该元素的所有内容读入字符串?然后通过标记具有相似 [System.Xml.Serialization.XmlElementAttribute("")] 属性的类使其反序列化到我预先存在的类中?
      • 因为现在我得到一个对象,其中包含所有主机及其组件和配置。虽然我想要一个特定主机的组件和配置列表:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多