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

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

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

JSONP:

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

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

我的风格:

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

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

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

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

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

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

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

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

 public static class JsonExtensions { public static T DeserializeEmbeddedJsonP(Stream stream) { using (var textReader = new StreamReader(stream)) return DeserializeEmbeddedJsonP(textReader); } public static T DeserializeEmbeddedJsonP(TextReader textReader) { using (var jsonReader = new JsonTextReader(textReader.SkipPast('('))) { var settings = new JsonSerializerSettings { CheckAdditionalContent = false, }; return JsonSerializer.CreateDefault(settings).Deserialize(jsonReader); } } } public static class TextReaderExtensions { public static TTextReader SkipPast(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 ,因为底层字段是可空的 。

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

样本工作.Net小提琴 。

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

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