XmlSerializer.Deserialize() ведет себя по-разному между NECF 2.0 и NETCF 3.5
Я перевожу старое приложение NETCF 2.0, которое использует веб-сервисы, в NETCF 3.5. Веб-сервис стороннего сервера остается без изменений. Также я недавно сгенерировал Reference.cs с использованием командной строки VS 2008 - и был рад увидеть тот же результат, что и при использовании VS 2005.
Мой вопрос касается определения пространства имен в корневом теге классом, который определяется веб-сервисом. Веб-служба Java сериализует его с атрибутом xmlns и XmlSerializer.Deserialize()
NETCF 3.5 обвиняет этот атрибут в возникновении исключения InvalidOperationException. Используя этот XML и XmlSerializer.Deserialize()
с NETCF 2.0 работает как положено. Объект десериализуется в память.
Позвольте нескольким фрагментам кода прояснить ситуацию.
исключение
InvalidOperationException-There is an error in XML document (2, 2).
InnerException: InvalidOperationException-<InstallDirective xmlns='java:my.foreign.namespace'> was not expected.
at System.Xml.Serialization.XmlSerializer.resolveDeserializingType(XmlReader reader, XmlSerializationReader serialReader, Boolean soap12)
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
at System.Xml.Serialization.XmlSerializer.Deserialize(Stream stream)
at My.Neat.Software.Bug.Test()
сгенерированный Reference.cs (отрывок)
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="java:my.foreign.namespace")]
public partial class InstallDirective {
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public System.Nullable<System.DateTime> Date;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public JustAnotherThing JustAnotherThing;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public System.Nullable<short> Oid;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string Filename;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string Version;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string Dir;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string Instructions;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("Scripts", IsNullable=true)]
public Script[] Scripts;
}
XML от веб-сервиса
<?xml version="1.0" encoding="utf-8"?>
<InstallDirective xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="java:my.foreign.namespace">
<Date>2014-02-11T13:31:49.238+01:00</Date>
<JustAnotherThing>
<FileName>MyFunnyFile.txt</CabFileName>
<Checksum>e759af8bd5787e3f8d62245a7d6aa73d</Checksum>
<FileExists xsi:nil="true" />
<Name>MyFunnyFile</Name>
<Version>1.2</Version>
<Path xsi:nil="true" />
<DependsOn xsi:nil="true" />
<RegistryPath xsi:nil="true" />
</JustAnotherThing>
<Oid>1</Oid>
<Filename>MyFunnyFile.txt</Filename>
<Version>1.2</Version>
<Dir>\My\Path\To\Files</Dir>
<Instructions xsi:nil="true" />
<Scripts xsi:nil="true" />
</InstallDirective>
фрагмент кода, который выдает исключение
FileInfo directiveFile = new FileInfo(@"\tmp\install\funnyInstallDirective.xml");
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InstallDirective));
TextReader reader = new StreamReader(directiveFile.OpenRead());
InstallDirective installDirective = (InstallDirective)xmlSerializer.Deserialize(reader); // BAM!
Спасибо за вашу помощь!
1 ответ
Действительно реализация XmlSerializer.Net Compact Framework 2.0 и 3.5 отличается. Решение этого "нового" поведения также легко. Поскольку XmlSerializer, который - в моем случае - сериализует XML как "фрагмент", нам нужно объявить XmlRootAttribute. Это связано с отсутствием аннотации XmlRootAttribute типа, который будет сериализован в файле Reference.cs. Для обратной совместимости с XML, сериализованным реализацией NETCF 2.0, нам нужно добавить пространство имен по определению InstallDirective (Reference.cs). К сожалению, я не нашел программного способа получить это.
До:
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InstallDirective));
FileStream file = File.Create(@"\tmp\install\funnyInstallDirective.xml");
TextWriter writer = new StreamWriter(file);
xmlSerializer.Serialize(writer, installDirective);
После:
XmlRootAttribute att = new XmlRootAttribute(typeof(InstallDirective).Name);
att.Namespace = "java:my.foreign.namespace";
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InstallDirective), att);
FileStream file = File.Create(@"\tmp\install\funnyInstallDirective.xml");
TextWriter writer = new StreamWriter(file);
xmlSerializer.Serialize(writer, installDirective);
Кредиты для ta.speot.is, кто ответил на вопрос здесь.
[ОБНОВЛЕНИЕ] Конечно, вы можете украсить его, используя аннотированный XmlTypeAttribute типа веб-службы. Это будет выглядеть так.
XmlTypeAttribute ta = (XmlTypeAttribute)Attribute.GetCustomAttribute(typeof(InstallDirective), typeof(XmlTypeAttribute));
XmlRootAttribute att = new XmlRootAttribute(typeof(InstallDirective).Name);
att.Namespace = ta.Namespace;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InstallDirective), att);