XmlSerializer десериализовать сбои - PullRequest
1 голос
/ 07 апреля 2010

У меня есть wsdl со стороннего сервера. Побежал svcutil и в конечном итоге с набором

XmlNode AMethod(object Request);

методы. Для каждого метода есть отдельный 100-страничный PDF-файл с описанием объектов ответа / запроса

Я думал обернуть веб-методы и использовать XmlSerializer для возврата строго типизированных объектов. Возвращенный XML выглядит следующим образом (я удалил заголовки мыла):

<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:type="ResponseExt" 
        xmlns="http://www.thirdparty.com/lr/">
  <Code>0</Code>
  <Message>SUCCESS</Message>
  <SessionId>session_token</SessionId>
</Response>

Выглядело просто. Создан класс (из документов / проводных захватов):

[XmlRoot("Response")]
//EDIT added XmlType
[XmlType("ResponseExt", Namespace = "http://www.thirdparty.com/lr/")]
public class MyClass {
    public string Code {get; set;}
    public string Message {get; set;}
    public string SessionId {get; set;}
}

Время обработки:

//XmlNode node = xml from above
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
XmlNodeReader reader =  new XmlNodeReader(node);
Myclass myclass = serializer.Deserialize(reader) as MyClass

Последняя строка, где он разрывается с внутренним сообщением об исключении: Указанный тип не был распознан: name = 'ResponseExt', namespace = 'http://www.thirdparty.com/lr/', at <Response xmlns=''>.
Я не могу понять, как сделать сериализатор счастливым и что именно эти два означают

XSI: тип = "ResponseExt" XMLNS = "http://www.thirdparty.com/lr/

Как всегда, любые советы и указатели приветствуются


РЕДАКТИРОВАТЬ: Принятый ответ ниже.

Я все еще получал исключение, пока не нашел это, надеюсь, это сэкономит кому-то время. Я начал работать задом наперед. Захваченный xml на проводе. Десериализован в мои созданные классы с правильными атрибутами: работал как шарм. Попробовал еще раз с веб-сервиса - исключение. По какой-то причине XmlSerializer не распознает ResponseExt.

XmlSerializer serializer = new XmlSerializer(typeof(Response));
XmlNode node = (XmlNode)results[0];
XmlDocument doc = new XmlDocument();
doc.LoadXml(node.OuterXml); //reload node
XmlNodeReader reader = new XmlNodeReader(doc.FirstChild); //there is only one node
Response rsp = serializer.Deserialize(reader) as Response; //works

РЕДАКТИРОВАТЬ: основная проблема файл wsdl не был завершен. Потратив на это 2 дня и найдя этот (уродливый) обходной путь, сторонний поставщик предоставил complete WSDL для всех типов, которые десериализовались без ошибок.

1 Ответ

1 голос
/ 07 апреля 2010

Почему вы вручную десериализуете XML, когда у вас есть WSDL?

Если у вас WSDL, используйте инструмент svcutil.exe или wsdl.exe, чтобы сгенерировать прокси-классы и DTO для сообщений XML, отправляемых и получаемых по проводам.

Смысл инструментария веб-сервисов, или «стека», заключается в том, чтобы предоставить это вам, чтобы вам не приходилось создавать классы и код XML-сериализации вручную.

Вы пробовали это? Вы пытались запустить WSDL с помощью одного из этих инструментов? Или вы пытались «Добавить веб-ссылку» в Visual Studio?


После обновления вопроса я предлагаю изменить WSDL, а не писать собственный код. Вы можете создать собственный WSDL для службы, который будет правильно генерировать прокси-классы, которые вы хотите. Если вам не нужны все 100 методов (или сколько их есть), то оставьте их. Если вам нужен пользовательский объект из метода, определите complexType, соответствующий этому объекту. Это намного проще и надежнее, чем код десериализации XML, созданный вручную для каждого метода.


Если вам не нравится эта идея, и вы хотите придерживаться ручного написания кода десериализации XML, то вам нужно сделать две вещи:

  1. присоединить пространство имен к атрибуту XmlRoot .

  2. измените имя вашего класса на ResponseExt и выведите его из класса с именем Response. Украсьте этот класс Response атрибутом XmlInclude . Это выравнивает использование сериализатора Xml с типом xsi:, используемым во фрагменте XML .

В коде это выглядит так:

[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
public class ResponseExt : Response {
}

[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
[XmlInclude(typeof(ResponseExt))]
public class Response {
    public string Code {get; set;}
    public string Message {get; set;}
    public string SessionId {get; set;}
}

public class XsiType
{
    public static void Main(string[] args)
    {
        try
        {
            string filename = "XsiType.xml";
            XmlSerializer s1 = new XmlSerializer(typeof(Response));
            ResponseExt r = null;
            using(System.IO.StreamReader reader= System.IO.File.OpenText(filename))
            {
                r= (ResponseExt) s1.Deserialize(reader);
            }

            var builder = new System.Text.StringBuilder();
            var xmlws = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
            using ( var writer = System.Xml.XmlWriter.Create(builder, xmlws))
            {
                //s1.Serialize(writer, r, ns);
                s1.Serialize(writer, r);
            }
            string xml = builder.ToString();
            System.Console.WriteLine(xml);

        }
        catch (System.Exception exc1)
        {
            Console.WriteLine("Exception: {0}", exc1.ToString());
        }
    }
}

related: Как заставить использовать атрибут xsi: type?

...