如何反序列化JSONP响应(最好使用JsonTextReader而不是字符串)?

时间:2018-01-26 23:24:05

标签: c# json json.net jsonp

我正在尝试使用声称返回JSON的Web服务,但实际上总是返回JSONP。我没有看到改变服务行为的方法。

我想使用NewtonSoft Json.Net来解析结果。我已经声明了一个类,让我们将其称为MyType,我想将内部JSON结果反序列化为。

JSONP:

parseResponse({
"total" : "13,769",
"lower" : "1",
"upper" : "20"})

正如您所看到的,这不是正确的JSON,因为它具有parseResponse(前缀和)后缀。虽然这个例子非常简单,但实际响应可能很长,大约为100K。

的MyType:

public class MyType
{
    public Decimal total;
    public int lower;
    public int upper;
}

将我的Web服务响应转换为流和JsonTextReader之后,我尝试反序列化,如下所示:

(MyType)serializer.Deserialize(jsonTextReader, typeof(MyType));

当然我得到一个结果为null,因为有一个带圆括号的讨厌的parseResponse。

我已经看了this question但遗憾的是没有帮助。我实际上使用JsonTextReader来输入JSON,而不是字符串(并且更喜欢这样以避免创建巨大字符串的性能损失)。即使我使用该问题的建议,它看起来很危险,因为它使用全局替换。如果没有好的方法来使用流,那么安全解析字符串的答案就可以了。

2 个答案:

答案 0 :(得分:20)

如果我将您的问题解释如下:

  

我正在尝试从Stream反序列化一些JSON。 " JSON"实际上是JSONP格式,因此包含一些我想忽略的前缀和后缀文本。如何在仍然直接从流中读取和反序列化而不是将整个流加载到字符串中时跳过前缀和后缀文本?

然后,您可以使用以下扩展方法从JSONP流反序列化JSON:

public static class JsonExtensions
{
    public static T DeserializeEmbeddedJsonP<T>(Stream stream)
    {
        using (var textReader = new StreamReader(stream))
            return DeserializeEmbeddedJsonP<T>(textReader);
    }

    public static T DeserializeEmbeddedJsonP<T>(TextReader textReader)
    {
        using (var jsonReader = new JsonTextReader(textReader.SkipPast('(')))
        {
            var settings = new JsonSerializerSettings
            {
                CheckAdditionalContent = false,
            };
            return JsonSerializer.CreateDefault(settings).Deserialize<T>(jsonReader);
        }
    }
}

public static class TextReaderExtensions
{
    public static TTextReader SkipPast<TTextReader>(this TTextReader reader, char ch) where TTextReader : TextReader
    {
        while (true)
        {
            var c = reader.Read();
            if (c == -1 || c == ch)
                return reader;
        }
    }
}

注意:

  • 在构建JsonTextReader之前,我构建了一个StreamReader并跳过了流中的第一个'('字符。这会将StreamReader定位在实际JSON的开头。

  • 在反序列化之前,我设置JsonSerializerSettings.CheckAdditionalContent = false告诉序列化程序在JSON内容结束后忽略任何字符。奇怪的是,有必要明确地这样做,尽管默认值似乎已经是false,因为underlying field is nullable

  • 通过将string传递给StringReader,可以使用相同的代码从DeserializeEmbeddedJsonP<T>(TextReader reader);反序列化嵌入的JSONP。这样做可以避免通过修剪前缀和后缀文本来创建新字符串,因此即使是较小的字符串也可以提高性能和内存使用率。

示例工作.Net fiddle

答案 1 :(得分:4)

它似乎正在返回JSONP。 Web服务默认会这样做有点奇怪,而不包括&#34;?callback&#34;。在任何情况下,如果只是这样,你可以轻松地使用RegEx去除方法调用:

var x = WebServiceCall();
x = Regex.Replace(x, @"^.+?\(|\)$", "");