Как я могу десериализовать XmlNode[] в тип 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;
}

И документ проекта содержит элемент xsd:anyType с именем config, который содержит следующий 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[] в тип Т?

2 ответа

Вот решение "Почему это не десериализовать как 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));

Есть решение, кажется, немного хакерский.

Кажется, есть волшебная функциональность для пространства имен 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;
    }
Другие вопросы по тегам