Сериализация WCF JSON несовместима, так как ее можно правильно десериализовать?

1

У меня есть сериализуемый полиморфный тип и контракт на обслуживание.

[DataContract]
[KnownType(typeof(SomethingA))]
[KnownType(typeof(SomethingB))]
public class Something
{
    [DataMember]
    public int Item1 { get; set; }

    [DataMember]
    public string Item2 { get; set; }
}

[DataContract]
public class SomethingA : Something
{ }

[DataContract]
public class SomethingB : Something
{ }

[ServiceContract]
[ServiceKnownType(typeof(SomethingA))]
[ServiceKnownType(typeof(SomethingB))]
public interface ITesting
{
    [OperationContract]
    [WebInvoke(
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "use-something",
        Method = "POST")]
    Something UseSomething();

    [OperationContract]
    [WebInvoke(
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "use-polymorphic-somethings",
        Method = "POST")]
    List<Something> UsePolymorphicSomethings();

    [OperationContract]
    [WebInvoke(
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "throw-web-exception",
        Method = "POST")]
    void ThrowServerException();
}

... и выполнение этого контракта.

using System.Net;
using System.ServiceModel.Web;

public class Testing : ITesting
{
    public Something UseSomething()
    {
        SomethingA a = new SomethingA();
        a.Item1 = 2;
        a.Item2 = "b";
        return a;
    }

    public List<Something> UsePolymorphicSomethings()
    {
        List<Something> retVal = new List<Something>();
        retVal.Add(new SomethingA { Item1 = 1, Item2 = "1" });
        retVal.Add(new SomethingB { Item1 = 1, Item2 = "1" });

        return retVal;
    }

    public void ThrowServerException()
    {
        try
        {
            throw new ApplicationException("Bad news.");
        }
        catch (ApplicationException e)
        {
            throw new WebFaultException<ApplicationException>(e, HttpStatusCode.InternalServerError);
        }
    }
}

У меня есть некоторые функциональные тесты в другой сборке.

using System.Net;
using System.Net.Http;
using System.Runtime.Serialization.Json;

[TestMethod]
public void UseSomething_WebTest()
{
    using (HttpClient http = new HttpClient())
    {
        http.BaseAddress = TestUtil.TestsBaseAddress;

        HttpResponseMessage response = http.PostAsJsonAsync("use-something",
            new StringContent(string.Empty)).Result;

        string ret1 = response.Content.ReadAsStringAsync().Result;

        // Deserializes SomethingA as Something.
        Stream stream = response.Content.ReadAsStreamAsync().Result;
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Something));
        Something ret = (Something)serializer.ReadObject(stream);

        // FAILS.
        Assert.AreEqual(typeof(SomethingA), ret.GetType());
    }
}

[TestMethod]
public void UsePolymorphicSomethings_WebTest()
{
    using (HttpClient http = new HttpClient())
    {
        http.BaseAddress = TestUtil.TestsBaseAddress;

        HttpResponseMessage response = http.PostAsJsonAsync("use-polymorphic-somethings",
            new StringContent(string.Empty)).Result;

        string ret2 = response.Content.ReadAsStringAsync().Result;

        // Deserializes SomethingA and SomethingB correctly.
        Stream stream = response.Content.ReadAsStreamAsync().Result;
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<Something>));
        List<Something> somethings = (List<Something>)serializer.ReadObject(stream);

        // SUCCEEDS.
        Assert.AreEqual(typeof(SomethingA), somethings[0].GetType());
        Assert.AreEqual(typeof(SomethingB), somethings[1].GetType());
    }
}

[TestMethod]
public void ThrowServerException1_WebTest()
{
    using (HttpClient http = new HttpClient())
    {
        http.BaseAddress = TestUtil.TestsBaseAddress;

        HttpResponseMessage response = http.PostAsync("throw-web-exception",
           new StringContent(string.Empty)).Result;

        Assert.AreEqual(HttpStatusCode.InternalServerError, response.StatusCode);

        string ret3 = response.Content.ReadAsStringAsync().Result;

        // Deserializes ApplicationException as Exception.
        Stream stream = response.Content.ReadAsStreamAsync().Result;
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Exception));
        Exception e = (Exception)serializer.ReadObject(stream);

        // FAILS.
        Assert.AreEqual(typeof(ApplicationException), e.GetType());
    }
}

В UseSomething_WebTest ret1:

"{\"Item1\":2,\"Item2\":\"b\"}"

... без информации о типе, встроенной неудивительно, DataContractJsonSerializer не может десериализовать правильный тип возвращаемого объекта.

В UsePolymorphicSomethings_WebTest ret2:

"[{\"__type\":\"SomethingA:#InSite8WebServiceLib\",\"Item1\":1,\"Item2\":\"1\"},{\"__type\":\"SomethingB:#InSite8WebServiceLib\",\"Item1\":1,\"Item2\":\"1\"}]"

... с информацией о типе, встроенной в форму записей __type, которые DataContractJsonSerializer правильно десериализует.

В ThrowServerException1_WebTest ret3:

"{\"ClassName\":\"System.ApplicationException\",\"Message\":\"Bad news.\",\"Data\":null,\"InnerException\":null,\"HelpURL\":null,\"StackTraceString\":\"   at InSite8WebServiceLib.Testing.ThrowServerException() in d:\\\\SvnSource\\\\Hammersmith\\\\trunk\\\\VisualStudioProjects\\\\InSite8WebService\\\\InSite8WebServiceLib\\\\Testing\\\\Testing.cs:line 74\",\"RemoteStackTraceString\":null,\"RemoteStackIndex\":0,\"ExceptionMethod\":\"8\\u000aThrowServerException\\u000aInSite8WebServiceLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\\u000aInSite8WebServiceLib.Testing\\u000aVoid ThrowServerException()\",\"HResult\":-2146232832,\"Source\":\"InSite8WebServiceLib\",\"WatsonBuckets\":null}"

... с информацией о типе, встроенной в форму записи ClassName, которую DataContractJsonSerializer не может десериализовать правильно.

Поэтому мой вопрос таков. Почему для одной конечной точки WCF WebHttp несколько методов, основанных на WebInvoke, возвращают несколько разных форматов JSON, только один из которых DataContractJsonSerializer может фактически десериализоваться и как его исправить, чтобы он работал правильно?

Теги:
serialization
wcf

1 ответ

0

Вы можете заставить DataContractJsonSerializer всегда генерировать информацию о типе, перегружая конструктор. Затем вы установите для параметра alwaysEmitTypeInformation значение True.

Дополнительная информация о MSDN

  • 0
    Я видел эту страницу прежде, чем нашел ее довольно запутанной. В контексте службы WCF сама библиотека служб не имеет представления об используемом формате передачи данных, который определяется конфигурацией и обрабатывается автоматически инфраструктурой WCF, и в этом весь смысл использования WCF. Поскольку библиотека сервиса не знает, является ли формат передачи JSON или XML, как код библиотеки сервиса может вручную выполнить необходимую сериализацию?
  • 0
    Если метод веб-сервиса возвращает тип CustomObject, как бы реализация этого метода, который вручную генерирует строку JSON, даже компилировалась, поскольку тогда не было бы возврата для CustomObject? Кажется, вам нужно переопределить конфигурацию жестко закодированной реализацией и удалить всю статическую типизацию из реализации веб-сервиса и определить каждый метод для возврата необработанного HttpResponse или чего-то еще.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню