WCF, XmlRoot и дополнительные параметры - PullRequest
0 голосов
/ 05 февраля 2010

У меня есть служба WCF, и я не могу использовать DataContracts, поскольку мне нужно больше контроля над XML, полученным и отправленным этой службе. Поэтому я использую XmlRoot и XmlElement ... проблема, с которой я сейчас сталкиваюсь, заключается в том, что мой класс, в который десериализуется принимающий xml, и сериализованный ответ должны иметь одинаковое корневое имя, и когда я пытаюсь установить оба эти класса с:

[XmlRoot(ElementName = "myRoot")] 

Я получаю сообщение о том, что корневое имя уже использовалось. Есть ли простой обходной путь для этого? Я попытался поместить свой класс ответа в отдельное пространство имен, но это не сработало.

Если некоторые переменные не заданы в моем классе ответа, который сериализуется, я не могу их сериализовать и вернуть в ответе ... есть ли опция, которую мне не хватает, чтобы сделать это ... Я смог сделать это с помощью DataContract, но не могу понять это с помощью XmlElements

1 Ответ

0 голосов
/ 28 июля 2010

Один из способов добиться этого - перехватить ответ XML и изменить имя корневого элемента на что-то уникальное перед его десериализацией. Это можно сделать довольно легко с помощью пользовательского IClientMessageFormatter и связанного с ним атрибута операции.

Я только что написал это, так что "мокрая краска" и все такое, но вот как это выглядит:

/// <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
}
...