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

How to deserialize a JSONP response (preferably with JsonTextReader and not a string)?

我正在尝试使用声称 return JSON,但实际上总是 returns JSONP 的 Web 服务。我看不到更改该服务行为的方法。

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

JSONP:

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

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

我的类型:

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,而不是字符串(并且更喜欢这样以避免创建巨大的字符串对性能造成影响)。即使我会使用该问题的建议,它看起来也很危险,因为它使用了全局替换。如果没有使用流的好方法,安全解析字符串的答案就可以了。

看起来它正在返回 JSONP。有点奇怪的是,默认情况下网络服务会这样做,而你不包括“?回调”。无论如何,如果情况就是这样,您可以轻松地使用 RegEx 来剥离方法调用:

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

如果我这样解读你的问题:

I am trying to deserialize some JSON from a Stream. The "JSON" is actually in JSONP format and so contains some prefix and postfix text I would like to ignore. How can I skip the prefix and postfix text while still reading and deserializing directly from stream rather than loading the entire stream into a string?

然后您可以使用以下扩展方法从 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 to tell the serializer to ignore any characters after the end of the JSON content. Oddly enough it is necessary to do this explicitly despite the fact that the default value seems to be false already, since the underlying field is nullable.

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

工作示例 .Net fiddle