Как сериализовать свойство типа Object с помощью XmlSerializer - PullRequest
6 голосов
/ 29 февраля 2012

У меня есть свойство:

public object Tag

, но оно может содержать конечное число типов, к сожалению, без базового типа (кроме типа объекта).Но когда я сериализую объект с этим свойством, он не сериализуется.Есть ли способ проинструктировать XmlSerializer с возможными типами?

Ответы [ 3 ]

10 голосов
/ 29 февраля 2012

Я не рекомендую это, но да, вы можете использовать [XmlElement] и т. Д., Чтобы сообщить ему о нескольких типах кандидатов для члена:

public class Test
{
    private static void Main()
    {
        var ser = new XmlSerializer(typeof (Test));
        var obj = new Test {Value = "abc"};
        ser.Serialize(Console.Out, obj);
        obj = new Test { Value = 123 };
        ser.Serialize(Console.Out, obj);
        obj = new Test { Value = 456.7F };
        ser.Serialize(Console.Out, obj);
    }

    [XmlElement("a", Type = typeof(int))]
    [XmlElement("b", Type = typeof(string))]
    [XmlElement("c", Type = typeof(float))]
    public object Value { get; set; }
}

Важные биты вывода (игнорируя всеxmlns / <?xml> и т. Д.):

<Test>
  <b>abc</b>
</Test>

<Test>
  <a>123</a>
</Test>

<Test>
  <c>456.7</c>
</Test>
1 голос
/ 06 декабря 2013

Я сделал это, реализовав интерфейс IXmlSerializable, записав тип объекта в качестве атрибута элемента.

  public void ReadXml(XmlReader reader)
  {
     reader.MoveToContent();

     Boolean isEmptyElement = reader.IsEmptyElement; 
     reader.ReadStartElement();
     if (!isEmptyElement)
     {

        // ...here comes all other properties deserialization

        object tag;
        if (ReadXmlObjectProperty(reader, "Tag", out tag))
        {
           Tag = tag;
        }
        reader.ReadEndElement();
     }
  }

  public void WriteXml(XmlWriter writer)
  {

     // ...here comes all other properties serialization

     WriteXmlObjectProperty(writer, "Tag", Tag);
  }

  public static bool ReadXmlObjectProperty(XmlReader reader, 
                                           string name,
                                           out object value)
  {
     value = null;

     // Moves to the element
     while (!reader.IsStartElement(name))
     {
        return false;
     }
     // Get the serialized type
     string typeName = reader.GetAttribute("Type");

     Boolean isEmptyElement = reader.IsEmptyElement; 
     reader.ReadStartElement();
     if (!isEmptyElement)
     {
        Type type = Type.GetType(typeName);

        if (type != null)
        {
           // Deserialize it
           XmlSerializer serializer = new XmlSerializer(type);
           value = serializer.Deserialize(reader);
        }
        else
        {
           // Type not found within this namespace: get the raw string!
           string xmlTypeName = typeName.Substring(typeName.LastIndexOf('.')+1);
           value = reader.ReadElementString(xmlTypeName);
        }
        reader.ReadEndElement();
     }

     return true;
  }
  public static void WriteXmlObjectProperty(XmlWriter writer, 
                                            string name,
                                            object value)
  {
     if (value != null)
     {
        Type valueType = value.GetType();
        writer.WriteStartElement(name);
        writer.WriteAttributeString("Type", valueType.FullName);
        writer.WriteRaw(ToXmlString(value, valueType));
        writer.WriteFullEndElement();
     }
  }

  public static string ToXmlString(object item, Type type) 
  {
     XmlWriterSettings settings = new XmlWriterSettings();
     settings.Encoding = Encoding.ASCII;
     settings.Indent = true;
     settings.OmitXmlDeclaration = true;
     settings.NamespaceHandling = NamespaceHandling.OmitDuplicates;

     using(StringWriter textWriter = new StringWriter()) 
     using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) 
     {
        XmlSerializer serializer = new XmlSerializer(type);
        serializer.Serialize(xmlWriter, item, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
        return textWriter.ToString();
     }
  }

Примечание: в коде я не использую пространство имен и кодировку ASCII, это необязательный выбор.

НТН, Cabbi

0 голосов
/ 07 апреля 2017

Вы также можете использовать [XmlInclude(typeof(YourType))] для класса , который содержит свойство объекта. Так что в случае ОП, это будет выглядеть так

[XmlInclude(typeof(PossibleClassOne))]
[XmlInclude(typeof(PossibleClassTwo))]
public class MyClass
{
   public object Tag { get; set; }
}

Таким образом, вы можете сохранить имя элемента <Tag> во всех случаях

...