无论内容类型如何,强制WCF Rest客户端使用Json反序列化器

时间:2012-11-05 02:36:40

标签: json wcf rest

如何强制WCF Rest客户端使用Json反序列化器而不考虑内容类型?

我正在通过WCF调用基于REST的Web服务。

服务返回JSON主体,但内容类型为“Application / xml”。 WCF框架现在给我XmlException。

public class MessageFormatter : IClientMessageFormatter
{
    private readonly IClientMessageFormatter _formatter;

    public MessageFormatter(IClientMessageFormatter formatter)
    {
        _formatter = formatter;
    }

    public object DeserializeReply(System.ServiceModel.Channels.Message message, object[] parameters)
    {
        return _formatter.DeserializeReply(message, parameters);
    }
}

_formatter.DeserializeReply抛出XmlException。我无法在任何地方找到任何强制json反序列化的例子。

编辑 - 鼠标悬停时的“消息”对象正在抛出“{...错误读取正文:System.Xml.XmlException:根级别的数据无效。第1行,位置1. ...}”

我的另一个项目中与另一个REST服务(Picasa Web服务)通信的同一个对象有一个看起来像是JSON对象的xml序列化版本?所以这个问题似乎在流程中更进一步。我需要找到这个对象的起源。我将继续使用MessageEncoder类。

编辑 - (添加更多信息)

public class MyBinding : WebHttpBinding
{
    public MyBinding(WebHttpSecurityMode mode)
        : base(mode)
    {

    }

    public override BindingElementCollection CreateBindingElements()
    {
        var result = base.CreateBindingElements();

        var replacements = result.OfType<MessageEncodingBindingElement>().ToList();
        foreach (var messageEncodingBindingElement in replacements)
        {
            var index = result.IndexOf(messageEncodingBindingElement);
            result.Remove(messageEncodingBindingElement);
            result.Insert(index, new MyMessageEncodingBindingElement(messageEncodingBindingElement));
        }

        return result;
    }
}

public class MyMessageEncodingBindingElement : MessageEncodingBindingElement
{
    private readonly MessageEncodingBindingElement _element;

    public MyMessageEncodingBindingElement(MessageEncodingBindingElement element)
    {
        _element = element;
    }

    public override BindingElement Clone()
    {
        var result = _element.Clone();

        if (result is MessageEncodingBindingElement)
            return new MyMessageEncodingBindingElement(result as MessageEncodingBindingElement);

        return result;
    }

    public override MessageEncoderFactory CreateMessageEncoderFactory()
    {
        return new MyMessageEncoderFactory(_element.CreateMessageEncoderFactory());
    }
}

即使在设置断点时命中构造函数和Clone方法,也永远不会调用CreateMessageEncoderFactory()方法。有帮助吗?我正在尝试设置自定义MessageEncoder和MessageEncoderFactory类来修改Message对象的实例化过程。

3 个答案:

答案 0 :(得分:2)

您可以使用WebContentTypeMapper。这是WebHttpBinding的属性,您可以自定义编码器从该绑定执行反序列化的方式,包括强制它始终使用JSON反序列化器,而不管传入消息的Content-Type。下面的代码显示了如何做到这一点。

public class StackOverflow_13225272
{
    [DataContract]
    public class Person
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public int Age { get; set; }

        public override string ToString()
        {
            return string.Format("Person[Name={0},Age={1}]", Name, Age);
        }
    }
    [ServiceContract]
    public interface ITest
    {
        [WebGet(ResponseFormat = WebMessageFormat.Json)]
        Person GetPerson(string responseContentType);
    }

    public class Service : ITest
    {
        public Person GetPerson(string responseContentType)
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = responseContentType;
            return new Person { Name = "John Doe", Age = 29 };
        }
    }
    class AllJsonContentTypeMapper : WebContentTypeMapper
    {
        public override WebContentFormat GetMessageFormatForContentType(string contentType)
        {
            return WebContentFormat.Json;
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
        host.Open();
        Console.WriteLine("Host opened");

#if USE_NETFX4
        // This works on .NET 4.0 and beyond
        WebHttpBinding binding = new WebHttpBinding();
        binding.ContentTypeMapper = new AllJsonContentTypeMapper();
#else
        // This works on .NET 3.5
        CustomBinding binding = new CustomBinding(new WebHttpBinding());
        binding.Elements.Find<WebMessageEncodingBindingElement>().ContentTypeMapper = new AllJsonContentTypeMapper();
        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(binding, new EndpointAddress(baseAddress));
#endif

        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(binding, new EndpointAddress(baseAddress));
        factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
        ITest proxy = factory.CreateChannel();

        Console.WriteLine("With JSON: {0}", proxy.GetPerson("application/json"));
        Console.WriteLine("With XML: {0}", proxy.GetPerson("application/xml"));

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

答案 1 :(得分:1)

这可能有用。

public class ForceJsonClientMessageFormatter : IClientMessageFormatter
{
    private readonly DataContractJsonSerializer _jsonSerializer;

    public ForceJsonClientMessageFormatter(Type responseType)
    {
        _jsonSerializer = new DataContractJsonSerializer(responseType);
    }

    public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
    {
        throw new NotImplementedException("This client message formatter is for replies only!");
    }

    public object DeserializeReply(Message message, object[] parameters)
    {
        string messageBody = message.GetBody<string>();

        using (MemoryStream messageStream = new MemoryStream(Encoding.UTF8.GetBytes(messageBody)))
        {
            messageStream.Seek(0, SeekOrigin.Begin);
            object deserializedObject = _jsonSerializer.ReadObject(messageStream);
            return deserializedObject;
        }
    }
}

public class ForceJsonWebHttpBehavior : WebHttpBehavior
{
    protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
    {
        return new ForceJsonClientMessageFormatter(operationDescription.Messages[1].Body.ReturnValue.Type);
    }
}

答案 2 :(得分:0)

我没试过,但我认为这会奏效。您可以创建一个自定义IClientMessageFormatter,将消息格式覆盖为Json,将其包装在行为中,然后将该行为应用于客户端端点配置。

public class ForceJsonClientMessageFormatterDecorator : IClientMessageFormatter
{
    private readonly IClientMessageFormatter _decoratedFormatter;
    public ForceJsonClientMessageFormatterDecorator(IClientMessageFormatter decoratedFormatter)
    {
        _decoratedFormatter = decoratedFormatter;
    }

    public object DeserializeReply(Message message, object[] parameters)
    {
        message.Properties[WebBodyFormatMessageProperty.Name] = new WebBodyFormatMessageProperty(WebContentFormat.Json);
        return _decoratedFormatter.DeserializeReply(message, parameters);
    }

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

public class ForceJsonWebHttpBehavior : WebHttpBehavior
{
    protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
    {
        IClientMessageFormatter decoratedFormatter = base.GetReplyClientFormatter(operationDescription, endpoint);
        return new ForceJsonClientMessageFormatterDecorator(decoratedFormatter);
    }
}