正在更新复杂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进行抽象(生成类),它将帮助您进行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));