2012-03-08 7 views
0

私はXMLを更新する関数を書いていますが、いくつかの問題があります。私はちょうど更新されたものに古いXElementを設定することができますが、それはforeachループでは機能しません(ただ1つのクエリ結果であるべきです)。私は各フィールドをハードコードするつもりでしたが、ループを使うことに決めました。しかし、それは部分に当たると、それはサブ要素だとき、それは単一の要素としてそれをすべて読み込み、ファイルを台無し複雑な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あなたがXElement.Valueを使用しますが、上の反復べきではありません

<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

あなたは抽象的に必要とする(作りますクラス)を使用すると、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クラスに

を追加します。あなたはXElement.Value Propertyを使用しないでくださいなどブール値、日時、

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のプロジェクトであることが重要であるように見えます。エクステンダーの使用がTypePadで実装されていないと思われます – Jason

+0

自分で試してみてください:http:// msdn.microsoft.com/en-us/library/ayybcxe5.aspx –

+0

または、例えばGetInt()、GetInt()などの 'GetString()'を使う 'GetInt()'、 'GetBool {return Convert.ToInt32(GetString(...)); } ' –

0

デバイス内にある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.ValueXElementの子孫であるすべてのXTextノードを連結した文字列です。 setの場合は、すべてのサブ要素を上書きします。続きを読むXElement.Value

1

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

その行が行う仕事が、私は、各フィールド、すなわちIP、ポートの行を行う必要がないように、私はループをした、と:

代わりに使用した値の行を試してみてくださいは、ソースサブ項目を処理することについては確信していません – Jason

0

のように、他の種類を作るためにそれらを変更することができます。 XElement.ReplaceWith Methodを使用してください。値のゲッター出力

この要素のテキスト内容のすべてを含む文字列。複数のテキストノードがある場合、それらは連結されます。

これは、下位要素のタグが削除されることを意味します。私はその後、

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