{"<user xmlns = ''> no se esperaba.} Deserialización de XML de Twitter

213

Estoy sacando el XML de Twitter a través de OAuth.

Estoy haciendo una solicitud a http://twitter.com/account/verify_credentials.xml , que devuelve el siguiente XML:

<?xml version="1.0" encoding="UTF-8"?>
<user>
  <id>16434938</id>
  <name>Lloyd Sparkes</name>
  <screen_name>lloydsparkes</screen_name>
  <location>Hockley, Essex, UK</location>
  <description>Student</description>
  <profile_image_url>http://a3.twimg.com/profile_images/351849613/twitterProfilePhoto_normal.jpg</profile_image_url>
  <url>http://www.lloydsparkes.co.uk</url>
  <protected>false</protected>
  <followers_count>115</followers_count>
  <profile_background_color>9fdaf4</profile_background_color>
  <profile_text_color>000000</profile_text_color>
  <profile_link_color>220f7b</profile_link_color>
  <profile_sidebar_fill_color>FFF7CC</profile_sidebar_fill_color>
  <profile_sidebar_border_color>F2E195</profile_sidebar_border_color>
  <friends_count>87</friends_count>
  <created_at>Wed Sep 24 14:26:09 +0000 2008</created_at>
  <favourites_count>0</favourites_count>
  <utc_offset>0</utc_offset>
  <time_zone>London</time_zone>
  <profile_background_image_url>http://s.twimg.com/a/1255366924/images/themes/theme12/bg.gif</profile_background_image_url>
  <profile_background_tile>false</profile_background_tile>
  <statuses_count>1965</statuses_count>
  <notifications>false</notifications>
  <geo_enabled>false</geo_enabled>
  <verified>false</verified>
  <following>false</following>
  <status>
    <created_at>Mon Oct 12 19:23:47 +0000 2009</created_at>
    <id>4815268670</id>
    <text>&#187; @alexmuller your kidding? it should all be &quot;black tie&quot; dress code</text>
    <source>&lt;a href=&quot;http://code.google.com/p/wittytwitter/&quot; rel=&quot;nofollow&quot;&gt;Witty&lt;/a&gt;</source>
    <truncated>false</truncated>
    <in_reply_to_status_id>4815131457</in_reply_to_status_id>
    <in_reply_to_user_id>8645442</in_reply_to_user_id>
    <favorited>false</favorited>
    <in_reply_to_screen_name>alexmuller</in_reply_to_screen_name>
    <geo/>
  </status>
</user>

Estoy usando el siguiente código para deserializar:

    public User VerifyCredentials()
    {
        string url = "http://twitter.com/account/verify_credentials.xml";
        string xml = _oauth.oAuthWebRequestAsString(oAuthTwitter.Method.GET, url, null);

        XmlSerializer xs = new XmlSerializer(typeof(User),"");

        MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml));

        return (User)xs.Deserialize(ms);
    }

Y tengo lo siguiente para mi Userclase:

 [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class User
{

    [XmlElement(ElementName = "id")]       
    public long Id { get; set; }

    [XmlElement(ElementName = "name")] 
    public string Name { get; set; }

    [XmlElement(ElementName = "screen_name")]       
    public string ScreenName { get; set; }

    [XmlElement(ElementName = "location")]       
    public string Location { get; set; }

    [XmlElement(ElementName = "description")]      
    public string Description { get; set; }

    [XmlElement(ElementName = "profile_image_url")]      
    public string ProfileImageUrl { get; set; }

    [XmlElement(ElementName = "url")]       
    public string Url { get; set; }

    [XmlElement(ElementName = "protected")]      
    public bool Protected { get; set; }

    [XmlElement(ElementName = "followers_count")]      
    public int FollowerCount { get; set; }

    [XmlElement(ElementName = "profile_background_color")]       
    public string ProfileBackgroundColor { get; set; }

    [XmlElement(ElementName = "profile_text_color")]       
    public string ProfileTextColor { get; set; }

    [XmlElement(ElementName = "profile_link_color")]       
    public string ProfileLinkColor { get; set; }

    [XmlElement(ElementName = "profile_sidebar_fill_color")]       
    public string ProfileSidebarFillColor { get; set; }

    [XmlElement(ElementName = "profile_sidebar_border_color")]      
    public string ProfileSidebarBorderColor { get; set; }

    [XmlElement(ElementName = "friends_count")]     
    public int FriendsCount { get; set; }

    [XmlElement(ElementName = "created_at")]     
    public string CreatedAt { get; set; }

    [XmlElement(ElementName = "favourties_count")]      
    public int FavouritesCount { get; set; }

    [XmlElement(ElementName = "utc_offset")]      
    public int UtcOffset { get; set; }

    [XmlElement(ElementName = "time_zone")]       
    public string Timezone { get; set; }

    [XmlElement(ElementName = "profile_background_image_url")]        
    public string ProfileBackgroundImageUrl { get; set; }

    [XmlElement(ElementName = "profile_background_tile")]        
    public bool ProfileBackgroundTile { get; set; }

    [XmlElement(ElementName = "statuese_count")]        
    public int StatusesCount { get; set; }

    [XmlElement(ElementName = "notifications")]       
    public string Notifications { get; set; }

    [XmlElement(ElementName = "geo_enabled")]       
    public bool GeoEnabled { get; set; }

    [XmlElement(ElementName = "Verified")]        
    public bool Verified { get; set; }

    [XmlElement(ElementName = "following")]
    public string Following { get; set; }

    [XmlElement(ElementName = "status", IsNullable=true)]
    public Status CurrentStatus { get; set; }

}

Pero cuando se deserializa el XML anterior, la aplicación arroja lo siguiente:

  • $ excepcion {"Hay un error en el documento XML (2, 2)."} System.Exception {System.InvalidOperationException}

  • InnerException {"<user xmlns = ''> no se esperaba."} System.Exception {System.InvalidOperationException}

Ahora he buscado y la mejor solución que puedo encontrar es agregar espacios de nombres en blanco al serializador cuando serializas el contenido, pero no lo estoy serializando, así que no puedo.

También tengo un código para recibir estados, que funciona bien.

Entonces, ¿alguien puede explicarme por qué está ocurriendo el error? ¿Y una posible solución?

Gracias por adelantado.

Lloydsparkes
fuente
En mi caso, fue por una declaración incorrecta de XmlSerializer. Así que revisa eso también.
Mangesh
Tuve que agregar el campo con XmlAttribute a la clase. Ver enlace
Stefan Varga

Respuestas:

264

Decora tu entidad raíz con el atributo XmlRoot que se usará en tiempo de compilación.

[XmlRoot(Namespace = "www.contoso.com", ElementName = "MyGroupName", DataType = "string", IsNullable=true)]

O especifique el atributo raíz al deserializar en tiempo de ejecución.

XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "user";
// xRoot.Namespace = "http://www.cpandl.com";
xRoot.IsNullable = true;

XmlSerializer xs = new XmlSerializer(typeof(User),xRoot);
david valentine
fuente
66
Puede agregar un atributo XmlRoot a la clase msdn.microsoft.com/en-us/library/83y7df3e(VS.71).aspx [XmlRoot (Namespace = "www.contoso.com", ElementName = "MyGroupName", DataType = "string", IsNullable = true)]
david valentine
39
XML distingue entre mayúsculas y minúsculas. "Usuario" y "usuario" son nombres diferentes.
John Saunders,
44
Voté en contra porque siento que la información de XmlRoot debería definirse en la clase misma, no donde se está deserializando. Por esta razón, la solución de Junto es, en mi opinión, superior.
GuiSim
44
@GuiSim Está asumiendo que el OP tiene control sobre la entidad original. Ambas respuestas son válidas. En mi caso, no puedo agregar XmlRoot a la entidad, ya que se usa como parte de un MessageContract. Usar el método anterior funciona para mi escenario.
Bronumski
44
@GuiSim No estoy en desacuerdo con lo que dijo el OP. Tengo el control completo de mi entidad raíz pero no puedo usar el atributo root porque entra en conflicto con el atributo MessageContract. Ambas respuestas son válidas y la alternativa se presentó en los comentarios. El punto es que estás votando una respuesta válida, no una respuesta incorrecta. Si no está de acuerdo con que es lo mejor, simplemente no vote.
Bronumski
135

Aún más fácil es agregar las siguientes anotaciones a la parte superior de su clase:

[Serializable, XmlRoot("user")]
public partial class User
{
}
Rebecca
fuente
Simple y directo
mayowa ogundele
25
XmlSerializer xs = new XmlSerializer(typeof(User), new XmlRootAttribute("yourRootName")); 
Ranadheer Reddy
fuente
2
Esto es fantástico para casos en los que, de lo contrario, tendría que depender de condicionales para los atributos.
Den Delimarsky
2
Muy simple y arreglado mi caso por completo. ¡Gracias!
AW
12

El mensaje de error es tan vago, para mí tenía este código:

var streamReader = new StreamReader(response.GetResponseStream());
var xmlSerializer = new XmlSerializer(typeof(aResponse));
theResponse = (bResponse) xmlSerializer.Deserialize(streamReader);

Observe que xmlSerializer se instancia con aResponse, pero al deserializarlo accidentalmente lo envié a bResonse.

Jeremy Thompson
fuente
2
Acabo de tener un problema similar. xmlserializer se inicializó a typeof (T) y estaba
enviando
1
Declaré XmlRoot(..)en la clase secundaria ClassBde la clase primaria ClassA. Usé en new XmlSerializer(typeof(ClassA)lugar de encendido ClassBy también fundí un objeto. Gracias por señalar!
Shishir Gupta
6

La solución más simple y mejor es usar el atributo XMLRoot en su clase, en la que desea deserializar.

Me gusta:

[XmlRoot(ElementName = "YourPreferableNameHere")]
public class MyClass{
...
}

Además, use el siguiente ensamblaje :

using System.Xml.Serialization;
Khawaja Asim
fuente
3
¿Alguien quiere dar una explicación? ¿Por qué se XmlRoot()necesita un atributo para solucionar este problema? Aquí hay 5 respuestas que dicen "solo agregue este código" y no una explicación. Las personas responden 7 años después y todavía es solo "Agregar este código XmlRoot". De todas las respuestas elegí la más nueva para pedir una explicación.
Quantic
Gracias @Quantic, XmlRoot le permite crear un nuevo valor xml en lugar de usar la raíz predeterminada, puede personalizarlo. Es una especie de decoración que agregará a su xml en el momento de la compilación. Eliminará algunas veces los problemas de compatibilidad. Espero que entiendas mi punto.
Khawaja Asim
5

Como dice John Saunders, verifique si los nombres de clase / propiedad coinciden con la mayúscula de su XML. Si este no es el caso, el problema también ocurrirá.

Luuk
fuente
2

Mi problema era que uno de mis elementos tenía el atributo xmlns:

<?xml version="1.0" encoding="utf-8"?>
<RETS ReplyCode="0">
    <RETS-RESPONSE xmlns="blahblah">
        ...
    </RETS-RESPONSE>
</RETS>

No importa lo que intenté, el atributo xmlns parecía estar rompiendo el serializador, por lo que eliminé cualquier rastro de xmlns = "..." del archivo xml:

<?xml version="1.0" encoding="utf-8"?>
<RETS ReplyCode="0">
    <RETS-RESPONSE>
        ...
    </RETS-RESPONSE>
</RETS>

¡y voilá! Todo funcionó.

Ahora analizo el archivo xml para eliminar este atributo antes de deserializar. No estoy seguro de por qué esto funciona, quizás mi caso es diferente ya que el elemento que contiene el atributo xmlns no es el elemento raíz.

Patrick Borkowicz
fuente
Su archivo especificó que RETS-RESPONSE estaba en el espacio de nombres "blahblah". Si eso no coincide con su esquema, el elemento no se habría encontrado. Además, agregar un espacio de nombres predeterminado también causa todo tipo de problemas de referencia.
Suncat2000
2

Lo único que funcionó en mi caso fue usar el código de David Valentine. Usando Root Attr. en la clase Persona no ayudó.

Tengo este simple Xml:

<?xml version="1.0"?>
<personList>
 <Person>
  <FirstName>AAAA</FirstName>
  <LastName>BBB</LastName>
 </Person>
 <Person>
  <FirstName>CCC</FirstName>
  <LastName>DDD</LastName>
 </Person>
</personList>

Clase C #:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Código de deserialización C # de un método principal:

XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "personList";
xRoot.IsNullable = true;
using (StreamReader reader = new StreamReader(xmlFilePath))
{
List<Person> result = (List<Person>)(new XmlSerializer(typeof(List<Person>), xRoot)).Deserialize(reader);
 int numOfPersons = result.Count;
}  
usuario2596085
fuente
2

En mi caso, mi xml tenía múltiples espacios de nombres y atributos. Así que usé este sitio para generar los objetos: https://xmltocsharp.azurewebsites.net/

Y usé el siguiente código para deserializar

 XmlDocument doc =  new XmlDocument();
        doc.Load("PathTo.xml");
        User obj;
        using (TextReader textReader = new StringReader(doc.OuterXml))
        {
            using (XmlTextReader reader = new XmlTextReader(textReader))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(User));
                obj = (User)serializer.Deserialize(reader);
            }
        }
JoR
fuente
El enlace proporcionó una gran ayuda para deserializar xml a la clase.
smr5
1

Mi problema fue que el elemento raíz en realidad tiene un xmlns = "abc123"

Así que tuve que hacer XmlRoot ("elementname", NameSpace = "abc123")

Mella
fuente
1

Todo lo anterior no funcionó para mí, pero esto fue: Verifique que el nombre del elemento raíz de la clase sea exactamente como el de mayúsculas y minúsculas XML .

shdr
fuente
1

Nada funcionó para mí por estos errores, EXCEPTO

... was not expected, 
... there is an error in XML document (1,2)
... System.FormatException Input String was not in correct format ...

Excepto de esta manera

1- Debe inspeccionar la respuesta xml como una cadena (la respuesta que está tratando de deserializar en un objeto)

2- Use herramientas en línea para no escapar cadenas y prettify / formateador xml

3- ASEGÚRESE de que la Clase C # (clase principal) que está tratando de asignar / deserializar la cadena xml a TIENE UN XmlRootAttribute que coincide con el elemento raíz de la respuesta.

Ejemplo:

Mi respuesta XML tenía el siguiente aspecto:

<ShipmentCreationResponse xmlns="http://ws.aramex.net/ShippingAPI/v1/">
       <Transaction i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"/>
           ....

Y la definición de clase C # + atributos era como:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="ShipmentCreationResponse", WrapperNamespace="http://ws.aramex.net/ShippingAPI/v1/", IsWrapped=true)]
public partial class ShipmentCreationResponse {
  .........
}

Tenga en cuenta que la definición de clase no tiene " XmlRootAttribute "

[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://ws.aramex.net/ShippingAPI/v1/", IsNullable = false)]

Y cuando trato de des serializar usando un método genérico:

public static T Deserialize<T>(string input) where T : class
{
    System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T));

    using (System.IO.StringReader sr = new System.IO.StringReader(input))
    {
        return (T)ser.Deserialize(sr);
    }
}





var _Response = GeneralHelper.XMLSerializer.Deserialize<ASRv2.ShipmentCreationResponse>(xml);

Estaba recibiendo los errores anteriores

... was not expected, ... there is an error in XML document (1,2) ...

Ahora, simplemente agregando el "XmlRootAttribute" que solucionó el problema para siempre y para todas las demás solicitudes / respuestas tuve un problema similar con:

[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://ws.aramex.net/ShippingAPI/v1/", IsNullable = false)]

..

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://ws.aramex.net/ShippingAPI/v1/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://ws.aramex.net/ShippingAPI/v1/", IsNullable = false)]
public partial class ShipmentCreationResponse
{
    ........
}
Adel Mourad
fuente