Добавлять к атрибуту в xml, новые элементы

Расскажите недалекому, пожалуйста, как добавлять новые элементы в xml файл, к определенному атрибуту. У нас идет форма. В ней мы вписываем Логин, пароль и и выбираем из списка пользователей, к кому мы прикрепим этот логин и пароль.

Это я к чему. Это логин и пароль от 1С. В комбобоксе находятся пользователи программы. У одного пользователя, может быть много аккаунтов 1с. И когда юзер заходит в программу, ему в форме выведутся только его пользователи.

Вот так выглядит структура xml файла

  <Users1CList>
<User1C Mother="Юзер программы 1">
  <USER1C>Логин1С</USER1C>
  <UserPass>Пароль1С</UserPass>
</User1C>
<User1C Mother="Юзер программы 2">
  <USER1C>Логин1С</USER1C>
  <UserPass>Пароль1С</UserPass>
</User1C>
    <User1C Mother="Юзер программы 3">
  <USER1C>Логин1С</USER1C>
  <UserPass>Пароль1С</UserPass>
</User1C>

И вот я хочу к Юзеру программы 1, добавить еще один профиль 1С. Не просто, чтобы вниз списка добавлялись новые значения, а мы потом бегали по всему файлу собирали совпадения, а именно к конкретному атрибуту. И выглядело это вот так

  <Users1CList>
<User1C Mother="Юзер программы 1">
  <USER1C>Логин1С</USER1C>
  <UserPass>Пароль1С</UserPass>
  <USER1C>Логин1С</USER1C>
  <UserPass>Пароль1С</UserPass>
</User1C>
<User1C Mother="Юзер программы 2">
  <USER1C>Логин1С</USER1C>
  <UserPass>Пароль1С</UserPass>
</User1C>
    <User1C Mother="Юзер программы 3">
  <USER1C>Логин1С</USER1C>
  <UserPass>Пароль1С</UserPass>
</User1C>

Форма добавления, выглядит вот так введите сюда описание изображения

Мы заполняем поля, добавляем в список и сохраняем его. Я использую сериализацию.

Вот так, выглядит кнопка сохранения

        Users1C users1C = new Users1C();
        foreach (ListViewItem item in lvUsers.Items)
        {
            if (item.Tag != null)
            {
                users1C.Users1CList.Add((User1C)item.Tag);
            }
        }
        SerializeXML(users1C);

Кнопка добавления в список тоже примитивная

 User1C user1C = new User1C(tbUsername.Text, tbNukeCode.Text, ((User)cbUserLogin.SelectedItem).Username);
        Add(user1C);

Метод Add

ListViewItem list = new ListViewItem(user1C.UserName);
        list.Tag = user1C;

        lvUsers.Items.Add(list);

Жду критики, спасибо.

User1C

[Serializable]
public class Users1C
{
    public Users1C() { }
    public List<User1C> Users1CList = new List<User1C>() { };
}
[Serializable]
public class User1C
{
    [XmlElement("USER1C")]
    public string UserName { get; set; }
    public string UserPass { get; set; }
    [XmlAttribute("Mother")]
    public string UserLogin { get; set; }

    public User1C() { }
    public User1C(string Username, string Userpass, string UserLogin)
    {
        this.UserName = Username;
        this.UserPass = Userpass;
        this.UserLogin = UserLogin;
    }
}

Сохранение

XmlSerializer xml = new XmlSerializer(typeof(Users1C));
            using (FileStream fs = new FileStream(pathUser1CFile, FileMode.OpenOrCreate))
            {
                xml.Serialize(fs, users1C);
            }

Ответы (1 шт):

Автор решения: Alexander Petrov

Жду критики, спасибо.

На протяжении многих лет я встречаю вопросы по XML, в которых путают элементы и атрибуты.

Элемент записывается в угловых скобках: <name>...</name>.
Атрибут состоит из пары ключ и значение: key="value".
Внутри элемента может быть множество других элементов и атрибутов.

Не путайте базовые понятия, используйте корректные термины XML!


XmlSerializer никак не использует атрибут [Serializable], поэтому его можно удалить. Чтобы не было путаницы, отмечу: атрибуты языка C# и атрибуты XML - разные вещи.


Параметр FileMode.OpenOrCreate при записи замените на FileMode.Create. В противном случае в файле может получиться некорректный результат, если будет записано меньше информации, чем было раньше.


У вас стоит задача в первую очередь найти определённого пользователя.
После того как данные загружены из xml-файла (десериализованы), это можно сделать так:

var user = users1C.Users1CList.First(u => u.UserLogin == "login1");

Далее нужно добавить новые данные этому пользователю. Это невозможно в рамках вашей модели: юзер имеет лишь одиночные свойства UserName и UserPass, а нужны коллекции.

Поэтому перепишем код.



Класс Users1C не имеет смысла, т. к. он содержит только одно свойство-список. Его следует удалить и использовать просто список List<User1C>

public class User1C
{
    [XmlAttribute("Name")]
    public string Name { get; set; }
    [XmlElement]
    public List<UserInfo> Info { get; set; }

    public User1C() { }
    public User1C(string name, string pass, string login)
    {
        Name = name;
        Info = new List<UserInfo>();
        Info.Add(new UserInfo { Pass = pass, Login = login });
    }
}

public class UserInfo
{
    public string Login { get; set; }
    public string Pass { get; set; }
}

Это модели классов, которые будем использовать. Обратите внимание на имена свойств: Name, Login, Pass - без приставки User, потому что и так понятно, что они относятся к пользователю. Лишний словесный шум не нужен.

При желании, добавьте конструкторы классу UserInfo.

Коллекция пользователей - просто список:

private List<User1C> _users;

Сериализатор создаём следующим образом:

XmlSerializer _serializer = new XmlSerializer(
    typeof(List<User1C>)
    /*, new XmlRootAttribute("UserC1List")*/
    );

И не нужно его создавать локально внутри метода, лучше сделать его полем класса (формы), чтобы был всего один экземпляр.

Уберите комментарий, чтобы задать желаемое имя корневому элементу.


Добавление пользователей в коллекцию:

_users.Add(new User1C("name1", "pass1", "login1"));
_users.Add(new User1C("name2", "pass2", "login2"));

Десериализация:

using (FileStream fs = new FileStream(filename, FileMode.Open))
{
    _users = (List<User1C>)_serializer.Deserialize(fs);
}

Поиск пользователя по имени:

var user = _users.First(u => u.Name == "name1");
user.Info.Add(new UserInfo { Pass = "pass3", Login = "login3" });

И добавляем ему новую информацию.


Сериализация:

using (FileStream fs = new FileStream(filename, FileMode.Create))
{
    _serializer.Serialize(fs, _users);
}

XML будет выглядеть следующим образом:

<?xml version="1.0"?>
<UserC1List xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <User1C Name="name1">
    <Info>
      <Login>login1</Login>
      <Pass>pass1</Pass>
    </Info>
    <Info>
      <Login>login3</Login>
      <Pass>pass3</Pass>
    </Info>
  </User1C>
  <User1C Name="name2">
    <Info>
      <Login>login2</Login>
      <Pass>pass2</Pass>
    </Info>
  </User1C>
</UserC1List>
→ Ссылка