XML Сериализованный динамический объект

Мне нужно построить набор динамически созданных узлов XML из объектов в следующем формате:

<Root>
    <Name>My Name</Name>
    <DynamicValues>
        <DynamicValue1>Value 1</DynamicValue1>
        <DynamicValue2>Value 2</DynamicValue2>
    </DynamicValues>
</Root>

Имя узлов в DynamicValues -tag неизвестно заранее. Моя первоначальная мысль заключалась в том, что это должно быть возможно с помощью Expando Object, например:

[DataContract]
public class Root
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public dynamic DynamicValues { get; set; }
}

инициализируя его значениями:

var root = new Root
                    {
                        Name = "My Name",
                        DynamicValues = new ExpandoObject()
                    };

root.DynamicValues.DynamicValue1 = "Value 1";
root.DynamicValues.DynamicValue2 = "Value 2";

а затем Xml-сериализуйте его:

string xmlString;

var serializer = new DataContractSerializer(root.GetType());
using (var backing = new StringWriter())
using (var writer = new XmlTextWriter(backing))
{
    serializer.WriteObject(writer, root);
    xmlString = backing.ToString();
}

Однако, когда я запускаю это, я получаю сообщение SerializationException:

"Тип" System.Dynamic.ExpandoObject "с именем контракта данных 'ArrayOfKeyValueOfstringanyType: http://schemas.microsoft.com/2003/10/Serialization/Arrays' не ожидается. Рассмотрите возможность использования DataContractResolver или добавьте типы, неизвестные статически списку известных типов - например, используя атрибут KnownTypeAttribute или добавив их в список известных типов, переданных DataContractSerializer. "

Любые идеи, как я могу это достичь?

Ответ 1

[Serializable]
public class DynamicSerializable : DynamicObject, ISerializable
{
    private readonly Dictionary<string, object> dictionary = new Dictionary<string, object>();

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dictionary[binder.Name] = value;

        return true;
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        foreach (var kvp in dictionary)
        {
            info.AddValue(kvp.Key, kvp.Value);
        }
    }
}

[KnownType(typeof(DynamicSerializable))]
[DataContract]
public class Root
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public dynamic DynamicValues { get; set; }
}

Вывод:

<Program.Root xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://
schemas.datacontract.org/2004/07/">
  <DynamicValues i:type="Program.DynamicSerializable">
    <DynamicValue1 xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:st
ring" xmlns="">Value 1</DynamicValue1>
    <DynamicValue2 xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:st
ring" xmlns="">Value 2</DynamicValue2>
  </DynamicValues>
  <Name>My Name</Name>
</Program.Root>

Ответ 2

Пробовал это в vb.net, но не получил требуемый вывод. Нужна помощь.

_ Открытый класс DynamicSerializable   Наследует System.Dynamic.DynamicObject   Реализует ISerializable

Private ReadOnly dict As New Dictionary(Of String, Object)

Public Overrides Function TrySetMember(binder As SetMemberBinder, value As Object) As Boolean
    If Not dict.ContainsKey(binder.Name) Then
        dict.Add(binder.Name, value)
    Else
        dict(binder.Name) = value
    End If
    Return True
End Function

Public Overrides Function TryGetMember(binder As GetMemberBinder, ByRef result As Object) As Boolean
    Return dict.TryGetValue(binder.Name, result)
End Function

Public Sub GetObjectData(info As SerializationInfo, context As StreamingContext) Implements ISerializable.GetObjectData
    For Each keyValPair In dict
        info.AddValue(keyValPair.Key, keyValPair.Value)
    Next
End Sub

Конечный класс

Используемый код:

Исключить корень как новый корень       root.name = "1"       root.DynamicValues ​​= Новый DynamicSerializable

    root.DynamicValues.DynamicValue1 = "Value1"
    root.DynamicValues.DynamicValue2 = "Value1"

    Dim serializer = New DataContractSerializer(Root.GetType)
    Dim backing As New StringWriter
    Dim writer As New XmlTextWriter(backing)
    serializer.WriteObject(writer, Root)

Выход: