Проблемы десериализации WCF JSON с пробелами - PullRequest
2 голосов
/ 29 декабря 2011

У меня есть служба WCF REST, которая принимает пользовательский аргумент DataContract в виде JSON, который может быть как супертипом, так и подтипом. Когда я передаю JSON, содержащий лишние пробелы, объект всегда десериализуется как супертип. Когда я удаляю все пробелы из JSON, объект десериализуется как подтип.

Вот пример:

[DataContract]
[KnownType(typeof(SubClass))]
public class SuperClass
{
    [DataMember]
    public string Message { get; set; }
}

[DataContract]
public class SubClass : SuperClass
{
    [DataMember]
    public string Extra { get; set; }
}

[ServiceContract]
public interface IService1
{
    [OperationContract]
    [WebInvoke]
    void LogMessage(SuperClass arg);
}

public class Service1 : IService1
{
    public void LogMessage(SuperClass arg)
    {
        if (arg is SubClass)
        {
            Debug.WriteLine("SubClass");
        }
        else if (arg is SuperClass)
        {
            Debug.WriteLine("SuperClass");
        }
    }
}

Если я отправлю следующее сообщение, служба напечатает SuperClass:

POST http://localhost:5763/Service1.svc/LogMessage HTTP/1.1
User-Agent: Fiddler
Content-Type: text/json
Host: localhost:5763
Content-Length: 86

{    "__type":"SubClass:#WhitespaceTest",    "Message":"Message",    "Extra":"Extra" }

Я получаю тот же результат, если я тоже "красиво печатаю" пакет, так что JSOn разбивается на несколько строк. Однако служба выведет SubClass, если я уберу пробелы следующим образом:

POST http://localhost:5763/Service1.svc/LogMessage HTTP/1.1
User-Agent: Fiddler
Content-Type: text/json
Host: localhost:5763
Content-Length: 73

{"__type":"SubClass:#WhitespaceTest","Message":"Message","Extra":"Extra"}

Я отладил вывод System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString() и заметил, что XML, сгенерированный из JSON, отличается между двумя пакетами:

<!-- First packet, deserializes to SuperClass -->
<root type="object">
    <__type type="string">SubClass:#WhitespaceTest</__type>
    <Message type="string">Message</Message>
    <Extra type="string">Extra</Extra>
</root>

<!-- Second packet, deserializes to SubClass -->
<root type="object" __type="SubClass:#WhitespaceTest">
    <Message type="string">Message</Message> 
    <Extra type="string">Extra</Extra> 
</root>

Итак, кажется, что пробел сбивает с толку десериализатор JSON. Кто-нибудь знает, почему это происходит и что я могу с этим поделать?

Ответы [ 2 ]

3 голосов
/ 29 декабря 2011

Это известная проблема, которая была исправлена ​​в платформе 4.5. К сожалению, вам, по сути, нужно убрать пробелы спереди объекта, если вы хотите использовать полиморфизм в текущей версии фреймворка. Один из способов сделать это - прочитать / записать JSON, используя устройство чтения / записи, созданное JsonReaderWriterFactory, так как оно уберет пробелы (или любую красивую печать) вокруг элементов.

public class StackOverflow_8661714
{
    [DataContract(Name = "SuperClass", Namespace = "WhitespaceTest")]
    [KnownType(typeof(SubClass))]
    public class SuperClass
    {
        [DataMember]
        public string Message { get; set; }
    }

    [DataContract(Name = "SubClass", Namespace = "WhitespaceTest")]
    public class SubClass : SuperClass
    {
        [DataMember]
        public string Extra { get; set; }
    }
    public static void Test()
    {
        string originalJson = "{    \"__type\":\"SubClass:WhitespaceTest\",    \"Message\":\"Message\",    \"Extra\":\"Extra\" }";
        byte[] originalJsonBytes = Encoding.UTF8.GetBytes(originalJson);
        MemoryStream writeStream = new MemoryStream();
        XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(writeStream, Encoding.UTF8, false);
        XmlDictionaryReader jsonReader = JsonReaderWriterFactory.CreateJsonReader(originalJsonBytes, 0, originalJsonBytes.Length, XmlDictionaryReaderQuotas.Max);
        jsonWriter.WriteNode(jsonReader, true);
        jsonWriter.Flush();
        Console.WriteLine(Encoding.UTF8.GetString(writeStream.ToArray()));
        writeStream.Position = 0;

        DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(SuperClass), new Type[] { typeof(SubClass) });
        object o = dcjs.ReadObject(writeStream);
        Console.WriteLine(o);
    }
}
3 голосов
/ 29 декабря 2011

К сожалению, у меня нет хорошего ответа на это. Мы столкнулись с той же проблемой и связались с Microsoft по этому поводу. Они настаивали на том, что то, что они делали, было нормально из-за проблем с производительностью, что смешно, если учесть, как работает весь конвейер.

Итак, наш опыт показывает, что пробелы во многих местах вызывают проблемы. Кроме того, если вы поместите поле __type где-нибудь, кроме первого, вы получите супертип.

Мой совет - взглянуть на другие решения JSON. К сожалению, я не в курсе альтернатив, или я бы предложил что-то.

Редактировать: Как указывает carlosfigueira, это, по-видимому, было исправлено в 4.5. Я еще не пробовал там.

...