繁体   English   中英

如何将XmlNode []反序列化为T类型?

[英]How can I deserialize an XmlNode[] into type T?

我们有从SoapUI项目Xsd生成的此类:

[System.CodeDom.Compiler.GeneratedCodeAttribute("MSBuild", "4.0.30319.18408")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://eviware.com/soapui/config")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://eviware.com/soapui/config", IsNullable=true)]
public partial class RestRequestStep : object, System.ComponentModel.INotifyPropertyChanged
{
    public RestRequest restRequest;
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string service;
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string resourcePath;
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string methodName;
}

并且项目文档包含一个名为config的xsd:anyType元素,该元素包含以下xml

<con:config service="api" resourcePath="xxx" methodName="GET" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <con:restRequest name="Request 1" mediaType="application/json">
        <con:settings>
            <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting>
        </con:settings>
        <con:endpoint>${#Project#CurrentEndpoint}</con:endpoint>
        <con:request/>
        <con:originalUri>http://localhost/</con:originalUri>
        <con:assertion type="Valid HTTP Status Codes" name="Valid HTTP Status Codes">
            <con:configuration>
                <codes>200</codes>
            </con:configuration>
        </con:assertion>
        <con:assertion type="Schema Compliance" name="Schema Compliance">
            <con:configuration>
                <definition/>
            </con:configuration>
        </con:assertion>
        <con:credentials>
            <con:authType>No Authorization</con:authType>
        </con:credentials>
        <con:jmsConfig JMSDeliveryMode="PERSISTENT"/>
        <con:jmsPropertyConfig/>
        <con:parameters>
            <entry key="connectionName" value="${#Project#ConnectionName}" xmlns="http://eviware.com/soapui/config"/>
        </con:parameters>
    </con:restRequest>
</con:config>

在该文档的包装器中, config属性的类型为object

在运行时, config的内容是一个XmlNode[]其中包含config元素的所有子节点。

我需要将XmlNode[]转换为应有的类型RestRequestStep

到目前为止,我已经知道了:

    public static T FromXml<T>(this IEnumerable<XmlNode> input)
    {
        T output;

        var type = typeof(T);
        var serializer = CreateSerializer(type);
        var doc = new XmlDocument();
        var rootAttribute = (XmlRootAttribute)type.GetCustomAttributes(true).Where(a => a is XmlRootAttribute).SingleOrDefault();
        string ns = null;
        if (rootAttribute != null)
        {
            ns = rootAttribute.Namespace;
        }
        doc.AppendChild(doc.CreateElement(type.Name, ns));
        foreach (var node in input)
        {
            var inode = doc.ImportNode(node, true);
            if (inode is XmlAttribute)
            {
                doc.DocumentElement.Attributes.Append((XmlAttribute)inode);
            }
            else
            {
                doc.DocumentElement.AppendChild(inode);
            }
        }

        output = (T)serializer.Deserialize(new StringReader(doc.OuterXml));
        return output;
    }

但这在行output = (T)serializer.Deserialize(new StringReader(doc.OuterXml)); 具有以下异常:

System.InvalidOperationException was unhandled
  Message=There is an error in XML document (1, 2).
  Source=System.Xml
  InnerException: System.InvalidOperationException
       Message=Namespace prefix 'con' is not defined.
       Source=System.Xml

OuterXml的内容最终显示为:

<RestRequestStep service="api" resourcePath="xxx" methodName="GET" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://eviware.com/soapui/config">
    <con:restRequest name="Request 1" mediaType="application/json" xmlns:con="http://eviware.com/soapui/config">
        <con:settings>
            <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/&gt;</con:setting>
        </con:settings>
        <con:endpoint>${#Project#CurrentEndpoint}</con:endpoint>
        <con:request/>
        <con:originalUri>http://localhost/</con:originalUri>
        <con:assertion type="Valid HTTP Status Codes" name="Valid HTTP Status Codes">
            <con:configuration>
                <codes xmlns="">200</codes>
            </con:configuration>
        </con:assertion>
        <con:assertion type="Schema Compliance" name="Schema Compliance">
            <con:configuration>
                <definition xmlns=""/>
            </con:configuration>
        </con:assertion>
        <con:credentials>
            <con:authType>No Authorization</con:authType>
        </con:credentials>
        <con:jmsConfig JMSDeliveryMode="PERSISTENT"/>
        <con:jmsPropertyConfig/>
        <con:parameters>
            <entry key="connectionName" value="${#Project#ConnectionName}" xmlns="http://eviware.com/soapui/config"/>
        </con:parameters>
    </con:restRequest>
</RestRequestStep>

并且config的内容不应该是RestRequestStep而不是XmlNode[]吗?

如何反序列化XmlNode[]到T型?

找到了解决方案,似乎有点不客气。

似乎xsi命名空间具有神奇的功能,即使con包含在``中的字符串中,它也会触发破坏反序列化程序的操作。

解决方案是忽略所有传入的xsi:type属性:

    public static T FromXml<T>(this IEnumerable<XmlNode> input)
    {
        T output;

        var type = typeof(T);

        XmlSerializer serializer = CreateSerializer(type);

        var doc = new XmlDocument();
        var rootAttribute = (XmlRootAttribute)type.GetCustomAttributes(true).Where(a => a is XmlRootAttribute).SingleOrDefault();
        string ns = null;
        if (rootAttribute != null)
        {
            ns = rootAttribute.Namespace;
        }
        doc.AppendChild(doc.CreateElement(type.Name, ns));
        foreach (var node in input)
        {
            if (node.Name != "type" && node.NamespaceURI != "http://www.w3.org/2001/XMLSchema-instance")
            {
                var inode = doc.ImportNode(node, true);
                if (inode is XmlAttribute)
                {
                    doc.DocumentElement.Attributes.Append((XmlAttribute)inode);
                }
                else
                {
                    doc.DocumentElement.AppendChild(inode);
                }
            }
        }

        output = (T)serializer.Deserialize(new StringReader(doc.OuterXml));
        return output;
    }

这是“为什么不将它反序列化为RestRequestStep吗?

XmlSerializer构造函数的重载之一是(Type, Type[]) ,其中Type[]是“期望类型”的数组。

所以现在我有:

    public static XmlSerializer CreateSerializer(Type incomingType, IEnumerable<Type> knownTypes = null)
    {
        XmlSerializer output;
        output = new XmlSerializer(incomingType, knownTypes.ToArray());
        return output;
    }

    public static T FromXml<T>(this string input, params Type[] knownTypes)
    {
        T output;
        var serializer = CreateSerializer(typeof(T), knownTypes);
        output = (T)serializer.Deserialize(new StringReader(input));
        return output;
    }

    var p = File.ReadAllText(@"testproject.xml").FromXml<Project>(typeof(RestRequestStep));

暂无
暂无

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

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