В XML-сериализации класса, производного от SerializableDictionary<T1,T2>, отсутствуют свойства производного класса

Я успешно использую сериализатор XML для сериализации Microsoft.VisualStudio.Services.Common.SerializableDictionary<T1, T2>.

Теперь я хочу добавить строку guid в SerializableDictionary и также сериализовать ее. Поэтому я создал следующий класс:

public class SerializableDictWithGuid<T1, T2> : SerializableDictionary<T1, T2>, IMyInterface
{
    [XmlElement(ElementName = idXml001)]
    public string Guid { get; set; } = string.Empty;
}

При сериализации SerializableDictWithGuid с помощью приведенного ниже кода строка GUID отсутствует в выходных данных XML.

public class XmlSerializeHelper<T> where T : class
{
    public static string Serialize(T obj)
    {
        try
        {
            System.Xml.Serialization.XmlSerializer xsSubmit = new System.Xml.Serialization.XmlSerializer(typeof(T));

            using (var sww = new StringWriter())
            {
                using (XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented })
                {
                    xsSubmit.Serialize(writer, obj);
                }

                return sww.ToString();
            }
        }
        catch (Exception ex)
        {
            logger.log(ex);
        }

        return string.Empty;
    }
}

Минимальный воспроизводимый пример

SerializableDictWithGuid <string, string> test = new SerializableDictWithGuid <string, string>() { Guid = "123" };
XmlSerializeHelper<SerializableDictWithGuid <string, string>>.Serialize(test);

Вопрос: как добавить свойства в SerializableDictionary и включить их в XML-сериализацию?

Я не могу найти никаких документов для Microsoft.VisualStudio.Services.Common.SerializableDictionar‌​y<T1, T2>, но поскольку XmlSerializer не поддерживает словари, SerializableDictionary<T1, T2>, вероятно, работает, реализуя IXmlSerializable, т. е. сериализует себя вручную, используя код, созданный вручную. Скорее всего, этот код просто не написан для обработки свойств подклассов. Но есть ли у вас ссылка на документ или справочный источник, чтобы перепроверить?

dbc 05.02.2023 17:11

К сожалению, я также не могу найти никакой документации. Я думаю, вы правы насчет IXmlSerializable. Можете ли вы предложить другой подход к сериализации словаря, включая мое свойство guid? Спасибо

chriscode 05.02.2023 17:55

Вы можете повторно реализовать сериализацию с нуля (хотя я уверен, что вы не хотите этого делать!). Или вы можете изменить свою модель данных, чтобы Guid был свойством класса-контейнера, а не самого словаря. Другие обходные пути зависят от того, как SerializableDictionary<,> реализован IXmlSerializable. Он сделал это явно? Методы виртуальные?

dbc 05.02.2023 18:06

Я, скорее всего, добавлю guid к объектам значений (например, MyClass) словаря, это то, что вы называете классом контейнера? Я могу проверить информацию о том, как реализован IXmlSerializable, но я думаю, что самый простой способ — добавить свойство к значениям словаря. Вы хотите добавить свой комментарий в качестве ответа? Спасибо

chriscode 05.02.2023 18:21

Готовы ли вы сериализовать Guid как атрибут, а не как элемент? Если да, то похоже SerializableDictionary<T1, T2>

dbc 05.02.2023 18:26

Мои знания о терминологии xml довольно ограничены, но Attribute также будет работать, пока я могу прочитать его из десериализованного объекта.

chriscode 05.02.2023 18:39

Атрибуты подходят для примитивных значений, таких как строки, целые числа, идентификаторы и т.п. Не подходят для сложных значений с несколькими атрибутами. См. Атрибут XML и элемент XML.

dbc 05.02.2023 18:45
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

XmlSerializerне поддерживает словари , поэтому SerializableDictionary<T1, T2> работает за счет реализации IXmlSerializable. То есть он сериализует себя вручную, используя написанный вручную код. Таким образом, этот код ничего не знает о свойствах подклассов и не имеет никакого кода для их автоматического обнаружения и сериализации.

К счастью, оказывается, что IXmlSerializable.ReadXml() и IXmlSerializable.WriteXml() не были реализованы явно, поэтому должна быть возможность повторно реализовать их через повторную реализацию интерфейса и по-прежнему вызывать методы базового класса. И хотя в целом сложно реализовать IXmlSerializable, если вы сделаете Guid атрибутом XML, а не элементом, реализация станет простой, потому что, хотя дизайн IXmlSerializable не позволяет производным классам внедрять дочерние элементы, он позволяет производным классам вводить пользовательские атрибуты:

public class SerializableDictWithGuid<T1, T2> : SerializableDictionary<T1, T2>, IMyInterface, IXmlSerializable // Re-implement IXmlSerializable
{
    const string idXml001 = "idXml001";
    
    public string Guid { get; set; } = string.Empty;
    
    public new void ReadXml(XmlReader reader)
    {
        reader.MoveToContent();
        // At this point the reader is positioned on the beginning of the container element for the dictionary, so we can get any custom attributes we wrote.
        Guid = reader.GetAttribute(idXml001);
        base.ReadXml(reader);
    }
    
    public new void WriteXml(XmlWriter writer)
    {
        // At this point the container element has been written but nothing else, so it's still possible to add some attributes.
        if (Guid != null)
            writer.WriteAttributeString(idXml001, Guid);
        base.WriteXml(writer);
    }
}

В результате получается XML, который выглядит так:

<SerializableDictWithGuidOfStringString idXml001 = "123">
  <item>
    <key>
      <string>hello</string>
    </key>
    <value>
      <string>there</string>
    </value>
  </item>
</SerializableDictWithGuidOfStringString>

Стандарт XML гласит, что

Атрибуты используются для связывания пар имя-значение с элементами.

В целом атрибуты подходят для фиксированных наборов простых, примитивных значений, таких как имена и идентификаторы. Сложные значения с несколькими внутренними свойствами или повторяющиеся значения (например, коллекции) должны быть дочерними элементами, а не атрибутами. Поскольку идентифицирующий Guid является примитивным значением, уместно использование атрибута. Видеть:

Демонстрационная рабочий пример здесь.

Другие вопросы по теме