以复杂的XML更新数据

时间:2012-03-08 20:02:07

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

我正在编写一个函数来更新我的XML并遇到一些问题。我希望我可以直接将旧的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>

4 个答案:

答案 0 :(得分:1)

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

如果有,请用.Any()替换洞foreach().Single()

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

然后我认为没有理由不这样做:

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

答案 1 :(得分:1)

你需要抽象(制作类)你的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;
}

答案 2 :(得分:0)

您不应使用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>

XElement.ValueXText的后代中所有XElement个节点的串联字符串。如果你set,则覆盖所有子元素。阅读XElement.Value

的更多信息

答案 3 :(得分:0)

请勿使用XElement.Value Property。使用XElement.ReplaceWith Method。价值的吸气者输出

  

包含此元素的所有文本内容的String。如果有多个文本节点,它们将被连接。

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

尝试使用此行代替使用值:

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