2012-03-08 62 views
0

我在寫一個函數來更新我的XML,並遇到一些問題。我希望我可以直接將舊的XElement設置爲更新的XElement,但這在foreach循環中不起作用(儘管應該只是一個查詢結果)。我打算對每個字段進行硬編碼,但後來決定使用循環。然而,當它擊中的部分,它的子元素它讀取它作爲一個單一的元素,攪亂了文件在複雜的XML中更新數據

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> 

回答

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類----

爲了與文件一致,並能正常工作我做了詳細的版本。您可以修改他們做出其他類型,如布爾,日期時間等

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; 
} 
+0

這看起來很重要,這是一個Windows Phone 7項目 - 擴展器使用的TypeDescriptor看起來並沒有實現 – Jason 2012-03-08 22:42:16

+0

您可以嘗試創建自己的:http:// msdn.microsoft.com/en-us/library/ayybcxe5.aspx – 2012-03-08 22:52:59

+0

或者,您可以創建使用GetString()例如int,GetInt()的GetInt(),GetBool() {return Convert.ToInt32(GetString(...)); }' – 2012-03-08 22:55:09

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.Value是是XElement的後裔所有XText節點的連接字符串。如果你set它,你因此覆蓋所有的子元素。瞭解更多關於XElement.Value

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; 
+0

這條線的工作,但我做了一個循環,這樣我就不用做每個字段,即IP,端口的線路,然後我不知道處理源項目 – Jason 2012-03-08 21:26:31

0

不要使用XElement.Value Property。使用XElement.ReplaceWith Method。值的getter輸出

一個字符串,其中包含此元素的所有文本內容。如果有多個文本節點,它們將被連接。

這意味着子元素的標籤將被剝離。

嘗試這一行,而不是使用值:

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