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

时间:2014-10-15 16:28:52

标签: c# xml xml-serialization deserialization xdt

我们从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类型?

2 个答案:

答案 0 :(得分:0)

得到一个解决方案,看起来有点hacky。

似乎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;
    }

答案 1 :(得分:0)

以下解决方案&#34;为什么不将其反序列化为RestRequestStep

XmlSerializer构造函数的一个重载是(Type, Type[]),其中Type[]是&#34;预期类型&#34;的数组。

所以现在我有:

    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));