xsi:反序列化 XML 时的类型问题



请原谅我不精通XML声明,以及从类和定义的角度来看必须做什么才能使序列化/反序列化工作。 我正在尝试反序列化以下 XML:

<?xml version="1.0" encoding="UTF-8"?>
<AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
   <Owner>
      <ID>946a0786-3840-4007-afe1-76f138a3d31c</ID>
   </Owner>
   <AccessControlList>
      <Grant>
         <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
            <ID>946a0786-3840-4007-afe1-76f138a3d31c</ID>
         </Grantee>
         <Permission>FULL_CONTROL</Permission>
      </Grant> 
   </AccessControlList>
</AccessControlPolicy>

引发以下异常:

The specified type was not recognized: name='CanonicalUser', namespace='http://s3.amazonaws.com/doc/2006-03-01/', at <Grantee xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>.

我将代码简化为以下内容以单独重现:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Text; 
using System.Xml.Serialization; 
namespace sandbox
{
    public partial class Program
    {
        static void Main(string[] args)
        {
            string xml =
                "<AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Owner><ID>946a0786-3840-4007-afe1-76f138a3d31c</ID></Owner><AccessControlList><Grant><Grantee xsi:type="CanonicalUser" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ID>946a0786-3840-4007-afe1-76f138a3d31c</ID></Grantee><Permission>FULL_CONTROL</Permission></Grant><Grant><Grantee xsi:type="CanonicalUser" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ID>946a0786-3840-4007-afe1-76f138a3d31c</ID></Grantee><Permission>READ</Permission></Grant><Grant><Grantee xsi:type="CanonicalUser" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ID>946a0786-3840-4007-afe1-76f138a3d31c</ID></Grantee><Permission>WRITE</Permission></Grant><Grant><Grantee xsi:type="CanonicalUser" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ID>946a0786-3840-4007-afe1-76f138a3d31c</ID></Grantee><Permission>READ_ACP</Permission></Grant><Grant><Grantee xsi:type="CanonicalUser" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ID>946a0786-3840-4007-afe1-76f138a3d31c</ID></Grantee><Permission>WRITE_ACP</Permission></Grant></AccessControlList></AccessControlPolicy>";
            try
            {
                AccessControlPolicy acp = DeserializeXml<AccessControlPolicy>(xml);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                Console.WriteLine(e.Message); 
            }
            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);
            } 
        }
    }
    [XmlRoot(ElementName = "AccessControlPolicy", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
    public class AccessControlPolicy
    {
        [XmlElement(ElementName = "Owner", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public Owner Owner { get; set; }
        [XmlElement(ElementName = "AccessControlList", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public AccessControlList AccessControlList { get; set; }
    }
    [XmlRoot(ElementName = "AccessControlList", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
    public class AccessControlList
    {
        [XmlElement(ElementName = "Grant", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public List<Grant> Grant { get; set; }
    }
    [XmlRoot(ElementName = "Grant", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
    public class Grant
    {
        [XmlElement(ElementName = "Grantee", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public Grantee Grantee { get; set; }
        [XmlElement(ElementName = "Permission", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public string Permission { get; set; }
    }
    [XmlRoot(ElementName = "Grantee", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
    public class Grantee
    {
        [XmlElement(ElementName = "ID", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public string ID { get; set; }
        [XmlElement(ElementName = "URI", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public string URI { get; set; }
        [XmlElement(ElementName = "DisplayName", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public string DisplayName { get; set; }
        [XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Xsi { get; set; }
        [XmlAttribute(AttributeName = "type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
        public string Type { get; set; }
    }
    [XmlRoot(ElementName = "Owner", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
    public class Owner
    {
        [XmlElement(ElementName = "ID", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public string ID { get; set; }
        [XmlElement(ElementName = "DisplayName", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
        public string DisplayName { get; set; }
    }
}

> XmlSerializer 使用 xsi:type 说明符解析反序列化的目标类。

由于目标class被命名为 Grantee因此您必须使用 XmlType 属性对其进行修饰,该属性将CanonicalUser指定为 TypeName

[XmlType(TypeName= "CanonicalUser")]
public class Grantee

请注意,您可以减少应用于类的xml namespaces,如下所示。

[XmlRoot(ElementName = "AccessControlPolicy", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/")]
public class AccessControlPolicy
{
    [XmlElement(ElementName = "Owner", IsNullable = true)]
    public Owner Owner { get; set; }
    [XmlElement(ElementName = "AccessControlList", IsNullable = true)]
    public AccessControlList AccessControlList { get; set; }
}

public class AccessControlList
{
    [XmlElement(ElementName = "Grant",  IsNullable = true)]
    public List<Grant> Grant { get; set; }
}

public class Grant
{
    [XmlElement(ElementName = "Grantee",  IsNullable = true)]
    public Grantee Grantee { get; set; }
    [XmlElement(ElementName = "Permission", IsNullable = true)]
    public string Permission { get; set; }
}

[XmlType(TypeName= "CanonicalUser")]
public class Grantee
{
    [XmlElement(ElementName = "ID", IsNullable = true)]
    public string ID { get; set; }
    [XmlElement(ElementName = "URI", IsNullable = true)]
    public string URI { get; set; }
    [XmlElement(ElementName = "DisplayName", IsNullable = true)]
    public string DisplayName { get; set; }
}

public class Owner
{
    [XmlElement(ElementName = "ID",  IsNullable = true)]
    public string ID { get; set; }
    [XmlElement(ElementName = "DisplayName", IsNullable = true)]
    public string DisplayName { get; set; }
}   

更新

如果您想使用不同的xsi:type说明符,例如。

<Grantee xsi:type="CanonicalUser" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Grantee xsi:type="AnotherUser" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

然后,您必须为两者声明一个类,并具有一个公共基类。

必须使用涉及的每个具体派生类的XmlIncludeAttribute来修饰基类。
必须对派生类应用XmlTypeAttribute

[XmlInclude(typeof(CanonicalUser))]
[XmlInclude(typeof(AnotherUser))]
public abstract class Grantee
{
    // ...
} 
[XmlType(TypeName= "CanonicalUser", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/")]        
public class CanonicalUser : Grantee
{}
[XmlType(TypeName = "AnotherUser", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/")]
public class AnotherUser : Grantee
{}

重命名您的类授予 CanonicalUser。

[XmlRoot(ElementName = "AccessControlList", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
public class AccessControlList
{
    [XmlElement(ElementName = "Grant", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
    public List<CanonicalUser> Grant { get; set; }
}
[XmlRoot(ElementName = "Grant", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
public class CanonicalUser
{
    [XmlElement(ElementName = "Grantee", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
    public Grantee Grantee { get; set; }
    [XmlElement(ElementName = "Permission", Namespace = "http://s3.amazonaws.com/doc/2006-03-01/", IsNullable = true)]
    public string Permission { get; set; }
}

最新更新