2010-02-05 6 views
0

J'ai un service WCF et je ne peux pas utiliser DataContracts car j'ai besoin de plus de contrôle sur le XML reçu et envoyé à ce service. En tant que tel, j'utilise XmlRoot, et XmlElement ... le problème que je rencontre maintenant est que ma classe que le xml de réception est désérialisé dans et la réponse sérialisée doivent tous deux avoir le même nom racine, et quand j'essaie de définir les deux de ces classes avec:WCF, XmlRoot et paramètres facultatifs

[XmlRoot(ElementName = "myRoot")] 

Je reçois une erreur disant que le nom de racine était déjà utilisé. Y a-t-il une solution de contournement simple pour cela? J'ai essayé de placer ma classe de réponse dans un espace de noms séparé, mais cela n'a pas semblé fonctionner.

Si certaines des variables ne sont pas définies dans ma classe de réponse qui est sérialisée, je ne les ai pas sérialisées et retournées dans la réponse ... y at-il une option qui me manque? J'ai pu le faire avec un DataContract, mais je n'arrive pas à le comprendre avec XmlElements

+0

Même problème ici, il doit y avoir un moyen de le faire .. –

+0

question similaire avec "ne peut pas être fait" réponse à: http://stackoverflow.com/questions/2417721/wcf-contracts-namespaces-and-serializationexceptions –

Répondre

0

Une façon d'y parvenir est d'intercepter la réponse XML et de changer le nom de l'élément racine en quelque chose d'unique avant qu'il ne soit désérialisé. Cela peut être fait assez facilement avec un IClientMessageFormatter personnalisé et un attribut associé sur l'opération.

Je viens d'écrire ce, donc « peinture fraîche » et tout, mais voici ce qui ressemble à:

/// <summary> 
/// An operation attribute that changes the XML root name in responses 
/// </summary> 
/// <example> 
///  
/// [ServiceContract] 
/// [XmlSerializerFormat] 
/// public interface IRedBlueServiceApi 
/// { 
///  [OperationContract] 
///  [WebGet(...)] 
///  [XmlChangeRoot("RedResponse")] 
///  RedResponse GetRed(); 
/// 
///  [OperationContract] 
///  [WebGet(...)] 
///  [XmlChangeRoot("BlueResponse")] 
///  BlueResponse GetBlue(); 
/// } 
/// 
/// [XmlRoot("RedResponse")] 
/// public class RedResponse 
/// {...} 
/// 
/// [XmlRoot("BlueResponse")] 
/// public class BlueResponse 
/// {...} 
/// </example> 
[DefaultProperty("NewRootElementName")] 
public class XmlChangeRootAttribute : Attribute, IOperationBehavior 
{ 
    public XmlChangeRootAttribute(string newRootElementName) 
    { 
     NewRootElementName = newRootElementName; 
    } 

    public string NewRootElementName { get; set; } 

    #region IOperationBehavior Members 

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) 
    { 
     // Inject our xml root changer into the client request/response pipeline 
     clientOperation.Formatter = new XmlRootChangeFormatter(clientOperation.Formatter, NewRootElementName); 
    } 

    #endregion 

    #region Unrelated Overrides 

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) 
    { 
    } 

    public void AddBindingParameters(OperationDescription operationDescription, 
            BindingParameterCollection bindingParameters) 
    { 
    } 

    public void Validate(OperationDescription operationDescription) 
    { 
    } 

    #endregion 
} 

/// <summary> 
/// A simple wrapper around an existing IClientMessageFormatter 
/// that alters the XML root name before passing it along 
/// </summary> 
public class XmlRootChangeFormatter : IClientMessageFormatter 
{ 
    private readonly IClientMessageFormatter _innerFormatter; 
    private readonly string _newRootElementName; 

    public XmlRootChangeFormatter(IClientMessageFormatter innerFormatter, string newRootElementName) 
    { 
     if (innerFormatter == null) 
      throw new ArgumentNullException("innerFormatter"); 

     if (String.IsNullOrEmpty(newRootElementName)) 
      throw new ArgumentException("newRootElementName is null or empty"); 

     _innerFormatter = innerFormatter; 
     _newRootElementName = newRootElementName; 
    } 

    #region IClientMessageFormatter Members 

    public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) 
    { 
     return _innerFormatter.SerializeRequest(messageVersion, parameters); 
    } 

    public object DeserializeReply(Message message, object[] parameters) 
    { 
     if (!message.IsEmpty) 
     { 
      var doc = XDocument.Load(message.GetReaderAtBodyContents()); 

      if (doc.Root == null) 
       throw new SerializationException("Could not find root in WCF messasge " + message); 

      // Change the root element name 
      doc.Root.Name = _newRootElementName; 

      // Create a new 'duplicate' message with the modified XML 
      var modifiedReply = Message.CreateMessage(message.Version, null, doc.CreateReader()); 
      modifiedReply.Headers.CopyHeadersFrom(message.Headers); 
      modifiedReply.Properties.CopyProperties(message.Properties); 

      message = modifiedReply; 
     } 

     return _innerFormatter.DeserializeReply(message, parameters); 
    } 

    #endregion 
} 
Questions connexes