1 votes

Problème de sérialisation vers XML en utilisant REST et WCF

J'ai un problème avec l'utilisation de REST et le retour de la réponse en XML. J'ai créé un service de base à partir du modèle et tout semble correct, mais lorsque je veux sérialiser ma classe et la renvoyer comme réponse, le service renvoie autre chose.

Jetez un coup d'œil :

[WebHelp(Comment = "Sample description for DoWork")]
[WebInvoke(UriTemplate = "DoWork")]
[OperationContract]
public SampleResponseBody DoWork(SampleRequestBody request)
{
    //TODO: Change the sample implementation here
    return new SampleResponseBody()
    {
        Value = String.Format("Sample DoWork response: '{0}'", request.Data)

    };
}

[WebHelp(Comment = "Returns order state based on client and order number")]
[WebInvoke(UriTemplate = "OrderStatus")]
[OperationContract]
public order_status OrderStatus(q_order_status request)
{   
    return new order_status() 
    {
        error_id = 0,
        client_acr = "client", 
        order_acr = "order"
    };
}

La première méthode est celle du modèle, la seconde est la mienne. Les structures de retour ressemblent à ceci :

public class SampleResponseBody
{
    [DataMember]
    public string Value { get; set; }
}

public class q_order_status
{
    public string client_acr;
    public string order_acr;
}

[DataContract]
[XmlSerializerFormat]
public class order_status
{
    [XmlAttribute]
    public int error_id;
    [XmlElement]
    public string error_desc;
    [XmlElement]
    public string order_acr;
    [XmlElement]
    public string client_acr;
}

Édité :

Lorsque je suis sur la page d'aide du kit REST, j'obtiens cet exemple de réponse pour les deux méthodes ce qui est faux (je ne devrais pas obtenir cela pour la deuxième méthode) :

<SampleResponseBody>
<Value>String content</Value>
</SampleResponseBody>

Quand j'appelle la première méthode comme ceci :

User-Agent: Fiddler
Host: ipv4.fiddler:4617
Content-Type: text/xml
Content-Length: 63

<SampleRequestBody>
<Data>bla bla</Data>
</SampleRequestBody>

Je reçois :

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Wed, 30 Sep 2009 09:41:20 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Content-Length: 141
Connection: Close

<SampleResponseBody xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Value>Sample DoWork response: 'bla bla'</Value></SampleResponseBody>

Whis est ok.

Lorsque j'appelle la deuxième méthode comme ceci :

User-Agent: Fiddler
Host: ipv4.fiddler:4617
Content-Type: text/xml
Content-Length: 115

<q_order_status>
<client_acr>String content</client_acr>
<order_acr>String content</order_acr>
</q_order_status>

Je reçois ceci :

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Wed, 30 Sep 2009 09:44:18 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Content-Length: 67
Connection: Close

<order_status xmlns:i="http://www.w3.org/2001/XMLSchema-instance"/>

Et il devrait retourner une instance sérialisée en XML de la classe order_status Qu'est-ce qui ne va pas ?

Merci d'avance.

Après l'édition : ok, le problème était que pour [OperationContract] XmlSerializer n'a pas été déclenché. [XmlSerializerFormat] doit être inséré juste après le [OperationContract] pour remplacer les valeurs par défaut DataContractSerializer .

3voto

marc_s Points 321990

Avec le kit de démarrage WCF REST, vous devriez être en mesure de créer une méthode qui renvoie un fichier de type XElement comme valeur de retour :

[WebHelp(Comment = "Returns order state based on client and order number")]
[WebInvoke(UriTemplate = "OrderStatus")]
[OperationContract]
public XElement OrderStatus(q_order_status request)
{   
  .....
}

Dans ce cas, l'implémentation de votre méthode pourrait ressembler à ceci :

public XElement OrderStatus(q_order_status request)
{   
    return new XElement("q_order_status",
                 new XAttribute("error_id", 0),
                 new XElement("client_acr", "client acr value"),
                 new XElement("order_acr", "order acr value")
           );
}

Cela renverrait un fragment XML comme celui-ci :

<q_order_status error_id="0">
  <client_acr>client acr value</client_acr>
  <order_acr>order acr value</order_acr>
</q_order_status>

De cette façon, tout est vraiment possible - c'est à vous de décider comment et quoi créer en termes de structure XML.

Marc

0voto

Philippe Points 2289

Je dirais que q_order_status doit être décoré avec l'attribut [DataContract] et que tous ses membres (ainsi que ceux de order_status) doivent être décorés avec l'attribut [DataMember]. Ou bien avez-vous délibérément omis ces éléments ?

Pouvez-vous essayer avec ce code order_status :

[DataContract]
public class order_status
{
    [DataMember]
    public int error_id;
    [DataMember]
    public string error_desc;
    [DataMember]
    public string order_acr;
    [DataMember]
    public string client_acr;
}

Remarque : je vous suggère également de suivre les conventions de dénomination .NET pour les classes et les membres, PascalCase et pas d'underscore. Si vous êtes limité à un nom xml donné, vous pouvez utiliser la fonction Nom de l'attribut DataContract/DataMember pour définir le nom xml. (par exemple : [DataContract(Name="order_status")] public class OrderStatus). ;)

0voto

ICS Points 146

Cette question (et la dernière modification) nous a aidé à résoudre un problème similaire. Au lieu de décorer des milliers d'éléments de données avec des attributs DataContract/DataMember et d'utiliser le DataContractSerializer (par défaut), nous avons découvert que si notre service WCF utilisait plutôt le XmlSerializerFormat, nous pouvions facilement désérialiser nos objets.

Pour ceux qui ont besoin d'un exemple :

[System.ServiceModel.ServiceContract]
public interface IRestService
{
    [System.ServiceModel.OperationContract]
    // Added this attribute to use XmlSerializer instead of DataContractSerializer
    [System.ServiceModel.XmlSerializerFormat(
        Style=System.ServiceModel.OperationFormatStyle.Document)]
    [System.ServiceModel.Web.WebGet(
        ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Xml,
        UriTemplate = "xml/objects/{myObjectIdentifier}")]
    MyObject GetMyObject(int myObjectIdentifier);
}

C'est comme ça qu'on désérialise les objets :

public static T DeserializeTypedObjectFromXmlString<T>(string input)
{
    T result;

    try
    {
        System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
        using (System.IO.TextReader textReader = new System.IO.StringReader(input))
        {
            result = (T)xs.Deserialize(textReader);
        }
    }
    catch
    {
        throw;
    }

    return result;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X