我们从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"><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"><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 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类型?
答案 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));