1

コンテンツタイプに関係なく、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オブジェクトのインスタンス化プロセスを変更しようとしています。

4

3 に答える 3

3

そのためにを使用することができますWebContentTypeMapper。これはのプロパティでありWebHttpBinding、着信メッセージのContent-Typeに関係なく、常にJSONデシリアライザーを使用するように強制するなど、そのバインディングからエンコーダーがデシリアライズを実行する方法をカスタマイズできます。以下のコードは、これを行う方法を示しています。

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();
    }
}
于 2012-11-05T16:15:05.233 に答える
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);
    }
}
于 2012-11-05T04:52:44.660 に答える
0

試したことはありませんが、うまくいくと思います。メッセージ形式をJsonに上書きするカスタムIClientMessageFormatterを作成し、それをビヘイビアーでラップしてから、そのビヘイビアーをクライアントエンドポイント構成に適用できます。

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);
    }
}
于 2012-11-05T03:23:40.727 に答える