В 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-сериализацию?

🤔 А знаете ли вы, что...
C# поддерживает множество парадигм программирования, включая процедурное, объектно-ориентированное и функциональное программирование.


1
54
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 является примитивным значением, уместно использование атрибута. Видеть:

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