We have this class generated from the SoapUI project 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;
}
And the project document contains an xsd:anyType element called config which contains the following 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>
And in the wrapper for that document the config
property is of type object
At runtime, the content of config
is an XmlNode[]
containing all the child nodes of the config
element.
I need to turn that XmlNode[]
into the type it should be, RestRequestStep
So far I've got this:
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;
}
But that fails on the line output = (T)serializer.Deserialize(new StringReader(doc.OuterXml));
with the following Exception:
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
The content of OuterXml
ends up as:
<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>
And shouldn't the content of config
be a RestRequestStep
instead of an XmlNode[]
?
How do I deserialize an XmlNode[]
into type T?
Got a solution, seems a bit hacky.
It seems there is magic functionality for the xsi namespace and even though con
is inside a string in `` it triggers something which breaks the deserializer.
Solution was to ignore any incoming xsi:type
attributes:
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;
}
Here's the solution to "Why doesn't it deserialize as a RestRequestStep
?
One of the overloads of the XmlSerializer
constructors is (Type, Type[])
, where the Type[]
is an array of "expected types".
So now I have:
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));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.