正在更新复杂XML中的数据

本文关键字:数据 XML 复杂 更新 | 更新日期: 2023-09-27 18:27:21

我正在编写一个函数来更新我的XML,但遇到了一些问题。我希望我可以直接将旧的XElement设置为更新的XElement,但这在foreach循环中不起作用(尽管应该只有一个查询结果)。我本来打算对每个字段进行硬编码,但后来决定使用循环。然而,当它到达部分及其子元素时,它会将其作为一个单独的元素读取,并将文件搞砸

var myDevice = from c in appDataXml.Descendants("device")
               where (string)c.Element("name") == App.CurrentDeviceName 
                      && (string)c.Element("ip") == App.CurrentIp
               select c;
if (myDevice.Any())
{
    foreach (XElement device in myDevice)
    {
        //device.Element("customname").Value = updatedDevice.Element("customname").Value;
        for (int a = 0; a < device.Elements().Count(); a++)
        {
            string field = device.Elements().ElementAt(a).Name.ToString();
            device.Element(field).Value = updatedDevice.Element(field).Value;
        }
    }
}

示例XML

<Devices>
  <device>
    <name>blah</name>
    <customname>custom</customname>
    <ip>192.168.1.100</ip>
    <port>1000</port>
    <sources>
      <source>
        <code>00</code>
        <name>blah2</name>
        <hidden>False</hidden>
      </source>
      <source>
      ...etc
    </sources>
  </device>

正在更新复杂XML中的数据

您需要对xml进行抽象(生成类),它将帮助您进行CRUD和搜索。

示例:(使用这些扩展:http://searisen.com/xmllib/extensions.wiki)

public class Device
{
    const bool ELEMENT = false;
    const bool ATTRIBUTE = true;
    XElement self;
    public Device(XElement self) { this.self = self; }
    public string CustomName 
    { 
        get { return self.Get("customname", string.Empty); }
        set { self.Set("customname", value, ELEMENT); }
    }
    public string Name { get { return self.Get("name", string.Empty); } }
    public string Ip { get { return self.Get("ip", "0.0.0.0"); } }
    public int Port { get { return self.Get("port", 0); } }
    public Source[] Sources
    {
        get { return _Sources ?? (_Sources = self.GetEnumerable("sources/source", xsource => new Source(xsource)).ToArray()); }
    }
    Source[] _Sources;
    public class Source
    {
        XElement self;
        public Source(XElement self) { this.self = self; }
        public string Code { get { return self.Get("code", string.Empty); } }
        public string Name { get { return self.Get("name", string.Empty); } }
        public bool Hidden { get { return self.Get("hidden", false); } }
    }
}

一个使用它的例子:

XElement xdevices = XElement.Load(file.FullName);
Device[] devices = xdevices.GetEnumerable("device", xdevice => new Device(xdevice)).ToArray();
var myDevice = devices
     .Where(d => d.Name == App.CurrentDeviceName 
              && d.Ip == App.CurrentIp);
foreach (Device device in myDevice)
{
    string name = device.Name;
    foreach (Device.Source source in device.Sources)
    {
        string sourceName = source.Name;
    }
    device.CustomName = "new name";
}
xdevices.Save(file.FullName);

这是思维方式的改变,因此,您不必担心如何引用值,而是创建一个类来读取/写入xml,而只需将数据获取/传递给一个类,然后由该类读取/写入xml。

编辑:----添加到XElementConversions类----

为了与文件保持一致,并能正常工作,我制作了详细的版本。您可以修改它们以使其成为其他类型,如bool、DateTime等。

public static int GetInt(this XElement source, string name, int defaultValue)
{
    source = NameCheck(source, name, out name);
    return GetInt(source, source.GetDefaultNamespace() + name, defaultValue);
}
public static int GetInt(this XElement source, XName name, int defaultValue)
{
    int result;
    if (Int32.TryParse(GetString(source, name, null), out result))
        return result;
    return defaultValue;
}

你的意思是var myDevice = ... select c;最多会产生1个元素吗?

如果是,则用.Single():替换孔.Any()foreach()

var myDevices = ... select c;
var myDevice = myDevices.Single();  // exception when Count != 1

然后我看不出为什么这不起作用:

 myDevice.Element("customname").Value = updatedDevice.Element("customname").Value;

您不应该使用XElement.Value,而应该迭代设备XElement中的XElement元素并更改它们的XText节点。或者,您可以使用XAttribute,这样就不需要制作那么多嵌套元素。

<Devices>
 <Device Name="blah" IP="192.168.1.100" Port="1000">
   <Sources>
     <Source Code="00" Name="blah2" Hidden="false"/>
   </Sources>
 </Device>
</Devices>

CCD_ 10是作为CCD_ 12的后代的所有CCD_。如果set,则会覆盖所有子元素。阅读XElement.Value 的更多信息

不要使用XElement.Value属性。使用XElement.ReplaceWith方法。Value的getter输出

一个字符串,包含该元素的所有文本内容。如果有多个文本节点,它们将被连接起来。

这意味着子元素的标记将被剥离。

尝试这一行,而不是使用值:

device.Element(field).ReplaceWith(updatedDevice.Element(field));