处理来自 API 的 JSON 中的语法错误
Posted
技术标签:
【中文标题】处理来自 API 的 JSON 中的语法错误【英文标题】:Handle syntax error in JSON coming from API 【发布时间】:2017-07-05 09:49:25 【问题描述】:想象一下 JSON (, instead of :
) 中的以下语法错误:
[
"name": "anna",
"email": "anna@gmail.com",
"town", "london"
,
...
]
我想知道是否可以处理此错误而不是获取异常、获取错误对象、更正错误并继续使用正确的版本。
这是我的 Angular 服务的一部分;我正在尝试获取文本而不是 JSON 数据,但它不起作用...
angular.module('mine', [])
.config(function($sceProvider)
// Completely disable SCE.
$sceProvider.enabled(false);
)
.config(['$sceDelegateProvider', function($sceDelegateProvider)
$sceDelegateProvider.resourceUrlWhitelist([
'self',
'http://www.mocky.io/v2/5807df4a10000004122b74e2'
]);
])
.config(function ($httpProvider)
$httpProvider.interceptors.push(function($q)
return
'request': function(config)
config.headers.Accept = 'text/plain';
return config;
,
'response': function(response)
try
// try to parse it
response.data = JSON.parse(response.data);
catch (ex)
// try to fix it
console.log("error " + ex);
console.log(response.data);
response.data = fixed_data : "data";
// return the corect data.
// note that the original response.data WILL BE CHANGED and this is expected.
return response;
;
);
)
angular.module('mine').factory('MyFactory', ['$http','$q', function MyFactory($http,$q)
return
getData: function()
var deferred = $q.defer(),
config =
params:
,
url="http://www.mocky.io/v2/5807df4a10000004122b74e2";
$http.jsonp(url,config)
.then(
function (response)
deferred.resolve(response.data);
,
function (error)
console.log(error);
return $q.reject('Error retrieving data');
);
return deferred.promise;
;
]);
有没有办法将上述承诺引导到成功回调中,检索错误的 JSON 并纠正它?根据上面的示例,我该如何编码?
或者更简单的方法,如何从 $http.jsonp
检索文本而不是 JSON 数据,以免被驱动到失败回调?
【问题讨论】:
如果它以字符串形式返回,您可能会做一个正则表达式并将其查找/更改为正确的 JSON 语法,然后像往常一样使用 JSON.parse 它。 是的,当然可以。 这里的问题是因为语法错误,不是JSON,也不是有效的JSON。另外,从良好实践的角度来看,这确实应该在服务器端进行处理。 是的,有可能,transforming the response。但请注意,JSON 标准之所以严格是有原因的,正确的处理方法是使用该标准来传输信息。 我希望能用一些代码提供更具分析性的答案 【参考方案1】:简短回答:不,没有。
长答案:如果您的 JSON 序列化在后端不起作用,您基本上必须自己解析一个字符串并构造一个新对象。没有图书馆可以为您做到这一点。想想不同的后端服务。
【讨论】:
【参考方案2】:我总体上同意@Florian 的回答 - 没有简单的方法。 我认为你应该:
想办法找到问题所在; 替换逗号; 重新解析为 JSON。找到问题所在的想法:在每一秒(偶数)值之后需要一个逗号。每一秒后(奇数 - 1., 3., 5) - 一个冒号。在每个新的 上,您必须开始一个新的计数。这可能很麻烦,但可行。
【讨论】:
如何将承诺重定向到成功回调中以执行上述所有操作?它进入失败回调... 不要将其视为 JSON。接受它作为一个字符串。收到后,尝试解析它以处理可能的错误(try/catch)。只有在 'catch' 中你才会出错。 既然我必须使用$http.jsonp,我怎样才能返回一个字符串而不是JSON?【参考方案3】:你可以使用 douglascrockford 的JSON-js。如果它不是一个有效的 json,它会抛出一个错误,以便你可以使用 try/catch 捕获并返回一个新的 Promise 或简单的 true/false。如果您不使用该库,它将回退到内置解析器。
$http(
method: "GET",
url: '../data/data-feed.json'
)
.then(
function (response)
console.log(response);
try
JSON.parse(json);
console.log("valid");
catch (e)
console.log("invalid");
// correct the invalid json here
,
function (error)
console.log('error');
);
默认 JSON 解析器行为
function parseJSON (jsonString)
try
var jString = JSON.parse(jsonString);
if (jString && typeof jString === "object")
return jString;
catch (e)
return false;
;
var inValidJson = '["name": "anna","email": "anna@gmail.com","town", "london"]';
var validJson = '["name": "anna","email": "anna@gmail.com","town": "london"]';
console.log("if invalid returns: ", parseJSON(inValidJson));
console.log("if valid get original object: ",parseJSON(validJson));
【讨论】:
【参考方案4】:TL;DR
进一步了解 OP 问题后编辑:
在您想要编辑响应内容的一般情况下,您可以使用“拦截器”来执行此操作,但响应开始时应该是合法的。也就是说,如果您想将数字字符串更改为其他正确 JSON 中的整数 - 这是可能的。
在 JSON 格式错误的情况下,OP 会出现问题 - 这是不可能的!
长篇大论
首先
进入经典XY problem!
您真的应该问自己为什么 JSON 会损坏,而不是尝试在客户端代码中修复它。 想想看 - 如果你现在修复它只会遇到更多问题,然后有人会修复 API - 然后 你 将有损坏的代码。
如果 JSON 应该是:
[
"name": "anna",
"email": "anna@gmail.com",
"addresses": ["town": "london", ..., ...]
,
...
]
或者(上帝保佑):
[
"name": "anna",
"email": ["anna@gmail.com","town", "london"]
,
...
]
你看 - 我的意思是 - API 坏了,它可以是任何东西。您应该修复 API。如果这个 API 不是你的来修复 -> 使用其他 API 或联系所有者来修复它。
JSONP
JSONP 是一种让 API 直接调用您的代码的方法。 您必须信任此 API。如果 API 会给我格式错误的 JSON,我会远离!
简而言之,JSONP 在 Angular(或实际上在任何地方)的工作方式是将<script>
标签注入到 DOM 中,src
指向 JSONp 请求的 URL。
服务器将填充带有函数名称的 JSON 数据(最常见的是 callback
,但它可以是任何全局可访问的函数(Angular 使用 angular.callbacks._xyz
)并将其发送。
浏览器然后调用从src
下载的脚本。
现在,问题是 浏览器 调用脚本。它不在 Angular 手中。而 that 正是 OP 面临的问题 - 脚本必须首先被评估为正确的 javascript,并且 浏览器 正在这样做,而不是 Angular。这是必须的。你不能进入它的中间。如果这样做,可能会带来安全风险。这就是为什么,例如,无论您要求什么,JSONP 请求的响应都会总是(按照惯例...)返回 MIME 类型为application/javascript
。
交战 - 这里是龙!
我劝你不要走这条路!
如果您坚持从 JSONP 调用中获取带有错误的 JSON(我所说的错误是指 JSON 可以被解析为对象但是有些东西您想更改该对象)您可以尝试添加“Interceptors”
.config(function ($httpProvider)
$httpProvider.interceptors.push(function($q)
return
'request': function(config)
// here you can edit the request.
return config;
,
'response': function(response)
// response.data will hold your bad data
// you could edit it
response.data = fix(response.data);
// return the correct data.
return response;
;
);
)
注意你也可以Overriding the Default Transformations
另外,请务必:
// Whitelist the JSONP endpoint that we are using to show that we trust it
.config(['$sceDelegateProvider', function($sceDelegateProvider)
$sceDelegateProvider.resourceUrlWhitelist([
'self',
'https://your.api.url/**'
]);
])
如果一切顺利,您将可以致电:
//uncomment jsonpCallbackParam: 'callback' if your jsonp callback
//parameter at the backend uses some other name but the default 'callback'
$http.jsonp(https://your.api.url/*,jsonpCallbackParam: 'callback'*/)
.then(function(response)
$scope.status = response.status;
$scope.data = response.data;
, function(response)
$scope.data = response.data || 'Request failed';
$scope.status = response.status;
);
【讨论】:
这不能回答我的问题!! 很抱歉你这么认为。 如果您可以更改以上内容以使用 $http.jsonp,那么赏金积分就是您的了。 @ILIAS 是对的!这实际上是你的问题。修复用户输入是一种糟糕的代码实践。 好的,我知道;但是,我想在特殊情况下那样工作。有人知道怎么做吗?【参考方案5】:这是可能的,但很麻烦。出于It-Z 的原因,通常最佳实践是修复 API 提供的 JSON 或寻找新的 API 来使用。假设您有不这样做的理由,以下是逻辑流程:
从 JSON.parse 中捕获错误并将其与未解析的响应字符串一起提供给新函数 在那个新功能中 如果您知道您的错误只会出现这种情况,请创建逻辑来查找并修复它;正则表达式将成为您的朋友 如果您试图捕捉多种类型的语法问题,您将需要更复杂的正则表达式和更复杂的逻辑 将更正后的字符串传回原来的 JSON.parse 函数【讨论】:
以上是关于处理来自 API 的 JSON 中的语法错误的主要内容,如果未能解决你的问题,请参考以下文章