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