简体   繁体   English

XML不反序列化为正确的类,并且之后不使用xsi:type属性进行序列化

[英]XML not deserializing to correct class, and not serializing with xsi:type attribute after

In the following snippet, I'm trying to deserialize XML that has type attributes on Animal records identifying them as either Cat or Dog , both of which inherit from Animal . 在以下代码段中,我尝试反序列化在Animal记录上具有类型属性的XML,以将它们标识为CatDog ,这两个属性均继承自Animal

When deserialized, these records all appear as Animal . 反序列化时,这些记录都显示为Animal

Then, when attempting to serialize the object (after deserialization), the xsi:type="Dog" and xsi:type="Cat" does not appear in the XML. 然后,当尝试序列化对象时(反序列化之后), xsi:type="Dog"xsi:type="Cat"不会出现在XML中。

I'm not sure if this is due to how I've decorated the classes or in my serializer/deserializer implementation. 我不确定这是由于我装饰类的方式还是在序列化器/反序列化器实现中。 Preferring if possible a solution in the class rather than the serializer/deserializer wrapper methods. 如果可能的话,最好选择类中的解决方案,而不要选择序列化器/解串器包装器方法。

Code: 码:

using System;  
using System.IO; 
using System.Text;
using System.Xml;
using System.Xml.Serialization; 

namespace sandbox
{
    public partial class Program
    {
        static void Main(string[] args)
        { 
            string xml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                "<OuterClass xmlns=\"http://myschema.com/\">" +
                "   <Animals>" +
                "      <Animal xmlns:xsi=\"http://myschema.com/\" xsi:type=\"Dog\">" +
                "         <Name>Watson</Name>" +
                "         <Height>10</Height>" +
                "         <Weight>10</Weight>" +
                "         <Paws>4</Paws>" +
                "         <Woof>True</Woof>" +
                "      </Animal>" +
                "      <Animal xmlns:xsi=\"http://myschema.com/\" xsi:type=\"Cat\">" +
                "         <Name>Hermy</Name>" +
                "         <Height>10</Height>" +
                "         <Weight>10</Weight>" +
                "         <Paws>4</Paws>" +
                "         <Meow>True</Meow>" +
                "      </Animal>" +
                "   </Animals>" +
                "</OuterClass>";

            OuterClass data = null;

            try
            {
                data = DeserializeXml<OuterClass>(xml);
                foreach (Animal curr in data.Animals) Console.WriteLine(curr.Name + ": " + curr.GetType().ToString());
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                Console.WriteLine(e.Message);
            }

            Console.WriteLine(SerializeXml(data));
            Console.ReadLine(); 
        }

        public static T DeserializeXml<T>(string xml)
        {
            XmlSerializer xmls = new XmlSerializer(typeof(T));
            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml))) 
                return (T)xmls.Deserialize(ms); 
        }

        public static string SerializeXml(object obj)
        { 
            XmlSerializer xml = new XmlSerializer(obj.GetType()); 
            using (MemoryStream stream = new MemoryStream())
            {
                using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8))
                {
                    xml.Serialize(writer, obj);
                    byte[] bytes = stream.ToArray();
                    return Encoding.UTF8.GetString(bytes, 0, bytes.Length); 
                }
            }
        }
    }

    [XmlRoot(ElementName = "OuterClass", Namespace = "http://myschema.com/", IsNullable = true)]
    public class OuterClass
    { 
        [XmlArrayItem(Type = typeof(Cat)), XmlArrayItem(Type = typeof(Dog)), XmlArrayItem(Type = typeof(Animal))] 
        public Animal[] Animals { get; set; }
        [XmlAttribute("type")]
        public string Type { get; set; }
    }

    [XmlType(TypeName = "Cat")]
    [XmlRoot(ElementName = "Animal", Namespace = "http://myschema.com/", IsNullable = true)]
    public class Cat : Animal
    { 
        public bool Meow { get; set; }
    }

    [XmlType(TypeName = "Dog")]
    [XmlRoot(ElementName = "Animal", Namespace = "http://myschema.com/", IsNullable = true)]
    public class Dog : Animal
    { 
        public bool Woof { get; set; }
    }

    [XmlInclude(typeof(Cat))]
    [XmlInclude(typeof(Dog))]
    [XmlRoot(ElementName = "Animal", Namespace = "http://myschema.com/", IsNullable = true)]
    public class Animal
    {
        public string Name { get; set; }
        public int Height { get; set; }
        public int Weight { get; set; }
        public int Paws { get; set; }
    }
}

Output: 输出:

Watson: sandbox.Animal       <-- should be sandbox.Dog
Hermy: sandbox.Animal        <-- should be sandbox.Cat
?<?xml version="1.0" encoding="utf-8"?>
<OuterClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://myschema.com/">
  <Animals>
    <Animal>                 <-- missing xsi:type, object missing 'Woof' 
      <Name>Watson</Name>
      <Height>10</Height>
      <Weight>10</Weight>
      <Paws>4</Paws>
    </Animal>
    <Animal>                 <-- missing xsi:type, object missing 'Meow'
      <Name>Hermy</Name>
      <Height>10</Height>
      <Weight>10</Weight>
      <Paws>4</Paws>
    </Animal>
  </Animals>
</OuterClass>

You need to create the classes correctly : 您需要正确创建类:

            OuterClass outerClass = new OuterClass()
            {
                Animals = new Animal[] {
                    new Dog() { Name = "Watson", Height = 10, Weight = 10, Paws = 4},
                    new Cat() { Name = "Hermy", Height = 10, Weight = 10, Paws = 4}
                }
            };

There are a couple of problems in your example: 您的示例中有两个问题:

  • the model does not match xmls 模型与xmls不匹配
  • every Animal element overrides xsi namespace 每个Animal元素都覆盖xsi命名空间
  • boolean values are not supported for serialization 布尔值不支持序列化

Please find additional details about problems below: 请在下面找到有关问题的其他详细信息:

1. model does not match xmls 1.模型与xmls不匹配

When you specify XmlArrayItem XmlSerializer will use type name as element name, or you can change it by providing ElementName explicitly. 当您指定XmlArrayItem时, XmlSerializer将使用类型名称作为元素名称,或者您可以通过显式提供ElementName进行更改。 If you annotate array property with XmlArrayItem you'll get the following output: 如果使用XmlArrayItem注释数组属性,将得到以下输出:

Console.WriteLine(SerializeXml(new OuterClass { Animals = new Animal[] { new Cat(), new Dog() } }));

<OuterClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://myschema.com/">
  <Animals>
    <Cat>
      <Height>0</Height>
      <Weight>0</Weight>
      <Paws>0</Paws>
      <Meow>false</Meow>
    </Cat>
    <Dog>
      <Height>0</Height>
      <Weight>0</Weight>
      <Paws>0</Paws>
      <Woof>false</Woof>
    </Dog>
  </Animals>
</OuterClass>

If you don't annotate, you'll get output with type defined by xsi:type attribute: 如果不加注释,则将获得由xsi:type属性定义的类型的输出:

<OuterClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://myschema.com/">
  <Animals>
    <Animal xsi:type="Cat">
      <Height>0</Height>
      <Weight>0</Weight>
      <Paws>0</Paws>
      <Meow>false</Meow>
    </Animal>
    <Animal xsi:type="Dog">
      <Height>0</Height>
      <Weight>0</Weight>
      <Paws>0</Paws>
      <Woof>false</Woof>
    </Animal>
  </Animals>
</OuterClass>

In this case you must add XmlInclude attribute to the base class. 在这种情况下,您必须将XmlInclude属性添加到基类。

2. every Animal element overrides xsi namespace 2.每个Animal元素都覆盖xsi命名空间

http://www.w3.org/2001/XMLSchema-instance is a special namespace defined in W3C that helps serializer to know what type is in the XML element. http://www.w3.org/2001/XMLSchema-instanceW3C中定义的特殊名称空间,可帮助序列化程序了解XML元素中的类型。 In your input xml, each Animal element overrides this namespace with a custom http://myschema.com/ so when serializer meets xsi:type="Cat" it has no idea what it means. 在您的输入xml中,每个Animal元素都会使用自定义http://myschema.com/覆盖此命名空间,因此当序列化程序遇到xsi:type="Cat"它根本不知道它的含义。 Wikipedia is a good starting point to read about XML namespaces: https://en.wikipedia.org/wiki/XML_namespace Wikipedia是阅读XML命名空间的一个很好的起点: https : //en.wikipedia.org/wiki/XML_namespace

3. Boolean values in XML 3. XML中的布尔值

W3C defines boolean data type as 'true', 'false', '0', and '1' so you'll get an exception if you'll deserialize boolean with value ' T rue'. W3C将布尔数据类型定义为“ true”,“ false”,“ 0”和“ 1”,因此,如果将值“ T rue”反序列化布尔值,则会出现异常。 You may find workaround options online but I suppose that your input XML is malformed and you need to lowercase boolean values in XML. 您可能会在网上找到解决方法选项,但我想您输入的XML格式错误,因此您需要将XML中的布尔值小写。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM