2009-01-27 8 views
17

J'utilise WCF pour renvoyer un document XML ancien (POX) à l'appelant. J'utilise le formateur XML Serializer pour transformer les objets en XML.Supprimer les espaces de noms xml de la réponse au repos WCF

Dans le document retourné, j'ai des références d'espace de noms XML supplémentaires (qui n'existaient pas dans la version ASMX) pour le schéma XML et l'instance. J'ai vu divers arguments sur le Web que ceux-ci ne devraient pas être enlevés que je n'apprécie pas pour renvoyer un document XML simple.

Quelle est la manière la plus simple de supprimer ces références xmlns d'un document XML renvoyé dans WCF?

La signature ressemble à:

public ResponseInfo Process(string input) { 
} 

Répondre

1

Dans mon service RESTful WCF que j'ai écrit avant le kit de démarrage RESTful WCF Je l'ai fait ce qui suit qui me donne de belles, les résultats propres.

Tout d'abord, assurez-vous que le comportement de point de terminaison webHttp est défini:

<endpointBehaviors> 
    <behavior name="Web"> 
     <webHttp/> 
    </behavior> 
    </endpointBehaviors> 

Votre point final devrait ressembler à ceci (moins le contrat pour la simplicité):

<endpoint address="" binding="webHttpBinding" behaviorConfiguration="Web" /> 

Mon contrat de service a ces opérations contrats en elle:

[WebGet(UriTemplate="tasks", ResponseFormat=WebMessageFormat.Xml)] 
    [OperationContract] 
    Task[] GetTasks(); 

    [WebGet(UriTemplate="tasks/{id}", ResponseFormat=WebMessageFormat.Xml)] 
    [OperationContract] 
    Task GetTask(string id); 

La mise en œuvre du service elle-même s rien de spécial à ce sujet. Vous pouvez essayer de modifier le WebMessageFormat mais le seul autre élément de l'énumération est "json".

+0

Ce serait bien si le downvoter laisserait un commentaire. Ma réponse est 100% correcte et réalisable. Il ne correspond peut-être pas exactement à ce que la personne demande, ou peut-être y a-t-il un meilleur moyen, mais cela fonctionne certainement. –

+0

Je n'étais pas le downvoter, mais bien que votre réponse soit "correcte", elle ne répond pas à cette question particulière. –

5

Je suppose que vous essayez au lieu d'obtenir quelque chose comme ça au début de votre xml:

<ResponseInfo 
    xmlns="http://schemas.datacontract.org/2004/07/ResponseInfo" 
    xmlns:i="http://www.w3.org/2001/XMLSchema-instance" > 

Vous voulez juste:

<ResponseInfo> 

Malheureusement, je ne l'ai pas vu un moyen facile encore pour enlever ces champs. Je cherchais des solutions et la plupart des options pour le supprimer nécessitent la création de votre propre inspecteur de message, ou votre propre encodeur.

19

Vous pouvez supprimer l'espace de noms XML en définissant le paramètre Namespace de l'attribut DataContract à une chaîne vide, comme ceci:

[DataContract(Namespace = "")] 
public class ResponseInfo 
{ 
    // ... 
} 

J'espère que cette aide ...

+1

Cela fonctionne mais vous devrez peut-être mettre des attributs [DataMember] sur les champs pour vous assurer que le xml n'est pas vide –

+20

De mes tests, cette méthode ne supprime pas les xmlns: i = "http://www.w3.org/2001/XMLSchema-instance "declaration –

4

Si vous voulez changer Xml, l'un des moyens est d'utiliser un XslTransform. J'ai eu un cas similaire, où j'avais besoin de supprimer les attributs xmlns d'une demande de poste Xml. Dans WCF, vous pouvez intercepter les messages Xml avant leur sortie ou avant leur traitement, en implémentant IClientMessageInspector ou IDispatchMessageInspector, selon que vous en avez besoin sur le client ou sur le serveur. côté.

Par exemple, pour dépouiller les attributs namespace d'un message Xml sortant à un service Web, je mis à exécution les IClientMessageInspector, en utilisant le code suivant:

#region IClientMessageInspector Members 
    public void AfterReceiveReply(ref Message reply, object correlationState) 
    { 
     //Console.WriteLine(reply.ToString()); 
    } 

    private XslCompiledTransform xt = null; 

    public object BeforeSendRequest(ref Message request, IClientChannel channel) 
    { 
     Console.WriteLine(request.ToString()); 
     if (!request.IsEmpty) 
     { 
      XmlReader bodyReader = 
       request.GetReaderAtBodyContents().ReadSubtree(); 

      MemoryStream ms = new MemoryStream(); 
      XmlWriter xw = XmlWriter.Create(ms); 

      if (xt == null) 
      { 
       xt = new XslCompiledTransform(true); 
       xt.Load("StripXmlnsi.xslt"); 
      } 
      xt.Transform(bodyReader, xw); 

      ms.Flush(); 
      ms.Seek(0, SeekOrigin.Begin); 

      bodyReader = XmlReader.Create(ms); 

      Message changedMessage = Message.CreateMessage(request.Version, null, bodyReader); 
      changedMessage.Headers.CopyHeadersFrom(request.Headers); 
      changedMessage.Properties.CopyProperties(request.Properties); 
      request = changedMessage; 
     } 
     return null; 
    } 
    #endregion 

et utilisé la transformation suivante:

<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="*"> 
    <!-- remove element prefix (if any) --> 
    <xsl:element name="{local-name()}"> 
     <!-- process attributes --> 
     <xsl:for-each select="@*"> 
     <!-- remove attribute prefix (if any) --> 
     <xsl:attribute name="{local-name()}"> 
      <xsl:value-of select="." /> 
     </xsl:attribute> 
     </xsl:for-each> 
     <xsl:apply-templates /> 
    </xsl:element> 
    </xsl:template> 
</xsl:stylesheet> 

J'espère que c'est utile.

+3

C'est une solution horrible. Pensez-vous vraiment qu'il est correct d'écrire tout ce code juste pour supprimer l'espace de noms? – user1662812

+6

Oui, la petite taille n'est pas égale à la beauté. Si vous avez une solution plus simple, n'hésitez pas à la poster, mais il ne suffit pas de rejeter quelque chose car il faut plus de 3 lignes de code. – Bernie

2

Juste pour donner à l'autre point de vue, si l'espace de noms est unique à votre projet, par exemple:

http://mycompany.com/myapi/

alors il devrait être conservé. De tels espaces de noms sont recommandés, ils ajoutent moins d'une ligne de code à tous les appels XPath (que vous pouvez transformer en méthode d'assistance) et nécessitent environ 15 lignes de code auxiliaire pour générer une carte de préfixe/URI, mais c'est le seul inconvénient et vous ne le rencontrerez pas toujours. En échange, vous obtenez des noms non ambigus pour chaque élément, ce qui signifie que vous pouvez composer des espaces de noms de tiers en toute impunité, par exemple. En théorie, vous pouvez renvoyer XHTML directement sans codage au niveau de l'application.

D'autres espaces de noms qui apparaissent ne poseront probablement pas de problème, car il est peu probable qu'ils soient utilisés sur l'un des tags que vous avez définis dans votre projet. En fait, s'ils le sont, il y a un bug quelque part. L'explication probable pour leur existence est que le framework les a ajoutés juste au cas où il aurait besoin d'ajouter un élément quelque part en dessous de l'endroit où ils sont déclarés, ce qui n'est peut-être pas un endroit dont vous devez vous préoccuper.

Une autre réponse a mentionné http://www.w3.org/2001/XMLSchema-instance ce qui est un peu spécial, peut-être pourriez-vous dire quels espaces de noms ont été ajoutés.

7

J'ai eu le même problème. Ajout de BodyStyle: = WebMessageBodyStyle.Bare à WebInvoke a fonctionné pour moi. La réponse n'est plus enveloppée dans une métadonnée.

+0

J'ai eu BodyStyl = WebMessageBodyStyle.Wrapper, et le changer en BodyStyle = WebMessageBodyStyle.Bare a résolu mon problème. – YoYoMyo

+0

Super scott! c'est parfait. Merci pour l'étiquette utile! –

+0

Cela devrait être beaucoup plus upvotes. Très bonne réponse. –

2

Je ne sais pas si cela va aider, mais nous avons eu 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 DataContractSerializer (par défaut), nous avons constaté que si notre service WCF utilisait XmlSerializerFormat à la place, nous pourrions facilement désérialiser nos objets.

[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); 
} 

Voici comment nous désérialiser 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; 
} 
3

J'ai trouvé une bonne solution à ce problème qui vous permet d'injecter votre XmlSerializer dans WCF qui est utilisé lors de la sérialisation et désérialisation demandes. Ce XmlSerializer peut être configuré pour omettre complètement les espaces de noms XML (y compris xmlns:i="w3.org/2001/XMLSchema-instance") ou de toute autre manière.

Ma solution utilise WcfRestContrib. Vous pourriez presque utiliser le POX formatter inclus mais dans notre cas, nous voulions prendre en charge les attributs, nous avons donc écrit notre propre formateur simple.

Instructions:

1) Référence WcfRestContrib de votre projet.

2) Créer une IWebFormatter mise en œuvre:

public class NamespacelessXmlFormatter : IWebFormatter { 
    public object Deserialize(WebFormatterDeserializationContext context, Type type) { 
     if (context.ContentFormat != WebFormatterDeserializationContext.DeserializationFormat.Xml) { 
      throw new InvalidDataException("Data must be in xml format."); 
     } 

     return NamespacelessXmlSerializer.Deserialize(context.XmlReader, type); 
    } 

    public WebFormatterSerializationContext Serialize(object data, Type type) { 
     using (var stream = NamespacelessXmlSerializer.Serialize(data, type)) { 
      using (var binaryReader = new BinaryReader(stream)) { 
       byte[] bytes = binaryReader.ReadBytes((int)stream.Length); 
       return WebFormatterSerializationContext.CreateBinary(bytes); 
      } 
     } 
    } 
} 

qui utilise un XmlSerializer qui correspond à vos besoins (est ici la nôtre qui omet simplement tous les espaces de noms):

public static class NamespacelessXmlSerializer { 

    private static readonly XmlSerializerNamespaces _customNamespace = new XmlSerializerNamespaces(); 

    private static readonly XmlWriterSettings _xmlSettings = new XmlWriterSettings { 
     OmitXmlDeclaration = true 
    }; 

    static NamespacelessXmlSerializer() { 
     // to make sure .NET serializer doesn't add namespaces 
     _customNamespace.Add(String.Empty, String.Empty); 
    } 

    /// <summary> 
    /// Deserializes object from its XML representation. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="stream"></param> 
    /// <returns></returns> 
    public static T Deserialize<T>(Stream stream) { 
     return (T)Deserialize(stream, typeof(T)); 
    } 

    /// <summary> 
    /// Deserializes object from its XML representation. 
    /// </summary> 
    public static object Deserialize(Stream stream, Type type) { 
     var ds = new XmlSerializer(type); 
     var d = ds.Deserialize(stream); 
     return d; 
    } 

    public static object Deserialize(XmlDictionaryReader xmlReader, Type type) { 
     var ds = new XmlSerializer(type); 
     var d = ds.Deserialize(xmlReader); 
     return d; 
    } 

    /// <summary> 
    /// Serializes object to XML representation. 
    /// </summary> 
    /// <exception cref="InvalidOperationException"> 
    /// Is thrown when there was an error generating XML document. This can happen 
    /// for example if the object has string with invalid XML characters: 
    /// http://www.w3.org/TR/2004/REC-xml-20040204/#charsets. 
    /// See this article for other potential issues: 
    /// http://msdn.microsoft.com/en-us/library/aa302290.aspx 
    /// </exception> 
    public static Stream Serialize<T>(T objectToSerialize) { 
     return Serialize(objectToSerialize, typeof(T)); 
    } 

    /// <summary> 
    /// Serializes object to XML representation. 
    /// </summary> 
    /// <exception cref="InvalidOperationException"> 
    /// Is thrown when there was an error generating XML document. This can happen 
    /// for example if the object has string with invalid XML characters: 
    /// http://www.w3.org/TR/2004/REC-xml-20040204/#charsets. 
    /// See this article for other potential issues: 
    /// http://msdn.microsoft.com/en-us/library/aa302290.aspx 
    /// </exception> 
    public static Stream Serialize(object objectToSerialize, Type type) { 
     var stream = new MemoryStream(); 

     XmlWriter writer = XmlWriter.Create(stream, _xmlSettings); 
     var x = new XmlSerializer(type); 
     x.Serialize(writer, objectToSerialize, _customNamespace); 

     stream.Position = 0; 

     return stream; 
    } 
} 

3) Appliquer les WebDispatchFormatter... attributs à votre service utilisant votre implémentation personnalisée comme type (basé sur ce documentation):

[WebDispatchFormatterConfiguration("application/xml")] 
[WebDispatchFormatterMimeType(typeof(NamespacelessXmlFormatter), "application/xml")] 

4) Appliquer l'attribut WebDispatchFormatter à toutes vos méthodes de service (sur la base de ce documentation).

5) C'est tout. Testez votre service et confirmez qu'il se comporte maintenant comme prévu.

+0

Cela ressemble à la réponse à moi. –

0

J'ai le même problème quand je travaille avec des clients ASMX et pour moi ce résoudre le problème:

Ajouter à votre interface de service:

[XmlSerializerFormat(Use = OperationFormatUse.Literal, Style = OperationFormatStyle.Document)] 

Ajouter aux opérations:

[OperationContract(Action = "http://www.YourNameSpace.com/ActionName",ReplyAction = "http://www.YourNameSpace.com/ActionName")] 
Questions connexes