Обновление данных в сложном XML - PullRequest
0 голосов
/ 09 марта 2012

Я пишу функцию для обновления моего 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 ]

1 голос
/ 09 марта 2012

Вы имеете в виду, что 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 голос
/ 09 марта 2012

Вам необходимо абстрагироваться (создать классы) вашего 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); } }
    }
}

Anпример его использования:

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;
}
0 голосов
/ 09 марта 2012

Не используйте XElement.Value Свойство .Используйте метод XElement.ReplaceWith .Получатель значения выводит

Строка, содержащая все текстовое содержимое этого элемента.Если есть несколько текстовых узлов, они будут объединены.

, что означает, что теги подэлементов будут удалены.

Попробуйте эту строку вместо использования значения:

device.Element(field).ReplaceWith(updatedDevice.Element(field));
0 голосов
/ 09 марта 2012

Вы не должны использовать 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 - это объединенная строка всех узлов XText, которые являются потомками XElement. Если вы set это, вы, следовательно, перезаписываете все подэлементы. Подробнее о XElement.Value

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...