Сериализуемый класс с динамическим набором полей

Дайте наводку как реализовать класс который можно будет сериализовать в такой XML:


<mcbmsg>
  <head>
      <ServiceName>SED</ServiceName>
      <RequestUID/>e6e9af2a-686e-0a88-9c22-0cc2b5250be8</RequestUID>
      <ReplyUID/>
      <ReferenceUID/> 
      <RequestDT>2022-07-21T12:12:12</RequestDT>
      <Sender>IBSO</Sender>
      <Receiver>TESSA</Receiver>
   </head>
   <body>
      <CardID>13C85CBF-320A-4C1A-89EE-31119ED564EF</CardID> 
      <IsAccepted>1</IsAccepted>
   </body>
</mcbmsg>

Проблема лишь в том что состав полей body постоянно меняется. т.е. секция head не меняется, а начинка body может быть разная.

пробовал реализовать через Idictionary но выдает ошибку при сериализации

namespace Shared.Model.Integration
    {
        [XmlRoot(ElementName = "head")]
        public class Head
        {
            [XmlElement(ElementName = "ServiceName")]
            public string ServiceName { get; set; }
            [XmlElement(ElementName = "RequestUID")]
            public string RequestUID { get; set; }
            [XmlElement(ElementName = "ReplyUID")]
            public string ReplyUID { get; set; }
            [XmlElement(ElementName = "ReferenceUID")]
            public string ReferenceUID { get; set; }
            [XmlElement(ElementName = "RequestDT")]
            public string RequestDT { get; set; }
            [XmlElement(ElementName = "Sender")]
            public string Sender { get; set; }
            [XmlElement(ElementName = "Receiver")]
            public string Receiver { get; set; }
        }

        [XmlRoot(ElementName = "Body")]
        public class Body
        {
            [XmlElement(ElementName = "Fields")]
            public Dictionary<string, object> Fields { get; set; }
        }

        [XmlRoot(ElementName = "Msg")]
        public class Msg
        {
            [XmlElement(ElementName = "head")]
            public Head Head { get; set; }
            [XmlElement(ElementName = "body")]
            public Body Body { get; set; }
        }
    }

помогите с реализацией body плз.


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

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

Задачу можно решить по-разному.

Создаём класс со всем набором возможных свойств. Навешиваем на эти свойства атрибут System.ComponentModel.DefaultValue. И задаём там значение по умолчанию, которое не будет сериализоваться.
У ссылочных типов дефолтное значение null.

using System.ComponentModel;
public class Body
{    
    public string CardID { get; set; }
    [DefaultValue(0)]
    public int IsAccepted { get; set; }

    [DefaultValue(0)]
    public int X { get; set; }
    [DefaultValue(0)]
    public int Y { get; set; }
}

Если задано:

Body = new Body
{
    CardID = "13C85CBF-320A-4C1A-89EE-31119ED564EF",
    IsAccepted = 1,
    // свойства X и Y равны дефолтному значению (0)
    // поэтому в XML их не будет
}

Результат будет:

<body>
  <CardID>13C85CBF-320A-4C1A-89EE-31119ED564EF</CardID>
  <IsAccepted>1</IsAccepted>
</body>

Если задано:

Body = new Body
{
    X = 3,
    Y = 4
}

Результат будет:

<body>
  <X>3</X>
  <Y>4</Y>
</body

Другой вариант - использовать свойства с суффиксом Specified.
XmlSerializer учитывает значение этих свойств в процессе работы. Если оно задано true - свойство с совпадающим именем будет сериализовано, иначе - нет.
Сами такие свойства помечаем атрибутом XmlIgnore.

public class Body
{
    public string CardID { get; set; }
    public int IsAccepted { get; set; }

    public int X { get; set; }
    public int Y { get; set; }

    [XmlIgnore]
    public bool CardIDSpecified { get; set; }
    [XmlIgnore]
    public bool IsAcceptedSpecified { get; set; }
    [XmlIgnore]
    public bool XSpecified { get; set; }
    [XmlIgnore]
    public bool YSpecified { get; set; }
}

Задавая эти свойства можно гибко настраивать список сериализуемых значений:

var body = new Body();
//...
body.CardIDSpecified = false;
body.IsAcceptedSpecified = false;
body.XSpecified = true;
body.YSpecified = true;

P.S. Также можно использовать методы с префиксом ShouldSerialize

→ Ссылка