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);
person schibbl    schedule 12.02.2014