admin管理员组

文章数量:1301600

Imagine the following syntax error in JSON (, instead of :):

[
    {
        "name": "anna",
        "email": "[email protected]",
        "town", "london"
    },
...
]

I am wondering if it is possible to handle this error instead of getting an exception, getting the erroneous object, correct the error and go on with the correct version.

Here is a part of my Angular service; I am trying to get text and not JSON data but it does not work...

 angular.module('mine', [])
    .config(function($sceProvider) {
        // Completely disable SCE.  
        $sceProvider.enabled(false);
    })
    .config(['$sceDelegateProvider', function($sceDelegateProvider) {
        $sceDelegateProvider.resourceUrlWhitelist([
        'self',
        ''
        ]);
    }])
  .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.jsonp(url,config)
                .then(              
                    function (response) {
                        deferred.resolve(response.data);
                    },  
                    function (error) {
                        console.log(error);
                        return $q.reject('Error retrieving data');
                    }
                );

                return deferred.promise;
            }
        };
 }]);

Is there anyway of directing the above promise into the success callback, retrieving the erroneous JSON and correcting it? How may I code that according to the above example?

Or maybe something easier, how to retrieve text and not JSON data from $http.jsonp as not to be driven to the failure callback?

Imagine the following syntax error in JSON (, instead of :):

[
    {
        "name": "anna",
        "email": "[email protected]",
        "town", "london"
    },
...
]

I am wondering if it is possible to handle this error instead of getting an exception, getting the erroneous object, correct the error and go on with the correct version.

Here is a part of my Angular service; I am trying to get text and not JSON data but it does not work...

 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;
            }
        };
 }]);

Is there anyway of directing the above promise into the success callback, retrieving the erroneous JSON and correcting it? How may I code that according to the above example?

Or maybe something easier, how to retrieve text and not JSON data from $http.jsonp as not to be driven to the failure callback?

Share edited Feb 22, 2017 at 2:05 It-Z 2,0101 gold badge27 silver badges36 bronze badges asked Feb 16, 2017 at 10:32 Unknown developerUnknown developer 5,97017 gold badges60 silver badges118 bronze badges 7
  • 3 If it es back as a string, you could probably do a regex and find/change it into proper JSON syntax, then just JSON.parse it as usual. – A. L Commented Feb 16, 2017 at 10:36
  • Yes, it's possible, of course. – dfsq Commented Feb 16, 2017 at 10:38
  • 1 The problem here is that because of the syntax error, it's not JSON, or valid JSON. Plus from a good practices point of view this should really be taken care of on the server side. – Craicerjack Commented Feb 16, 2017 at 10:38
  • 1 Yes, it is possible, by transforming the response. Note, however, that the JSON standard is strict for a reason, and the correct way to deal with this is to use the standard to transfer information. – lonesomeday Commented Feb 16, 2017 at 10:40
  • I 'd appreciate a more analytical answer with some code – Unknown developer Commented Feb 16, 2017 at 10:49
 |  Show 2 more ments

5 Answers 5

Reset to default 7 +50

TL;DR

Edits after further understanding the OP problem:

In the generic case where you want to edit content of a response you can do it with "Interceptors" yet the response should be legitimate to begin with. That is, if you wanted to change numeric strings to integers in an otherwise correct JSON - it would be possible.

In the situation the OP is heaving where the JSON is malformed - it is just not possible!

The long story

First

Getting into a classic XY problem!

You should really ask yourself why is the JSON broken and not attempt to fix it in the client code. Think of it - You will only get into more problems if you fix it now, and later someone will fix the API - then you will have the broken code.

What if the JSON should have been:

[
    {
        "name": "anna",
        "email": "[email protected]",
        "addresses": [{"town": "london", ...}, ...]
    },
...
]

Or (god forbid):

[
    {
        "name": "anna",
        "email": ["[email protected]","town", "london"]
    },
...
]

You see - my point is - The API is broken, it can be anything. You should fix the API. And if this API is not yours to fix -> use some other API or contact the owner to fix it.

JSONP

JSONP is a way to let APIs to call directly into your code. You must trust this API. If an API would have giving me malformed JSONs - I would stay away!

In short, the way JSONP works in Angular (or everywhere actually) is by injecting a <script> tag into the DOM with src pointing to the URL of the JSONp request.

The server will pad the JSON data with a function name (most often callback but it can be any globally accessible function (Angular is using angular.callbacks._xyz) and will send it.

The browser then invokes the script that was downloaded from the src.

Now, the thing is that it is the browser calling the script. It is not in the hands of Angular. And that is exactly the problem the OP is confronting - the script must be evaluated as a correct JavaScript to begin with and the browser is doing that, not Angular. It is a MUST. You cannot get in the middle of it. It could pose a security risk if you do. This is why, for instance, the response of a JSONP request will always (by convention...) be returned with MIME type of application/javascript no matter what you ask for.

Warring - here be dragons!

I urge you not to go in this path!

If you are insisting in getting from a JSONP call a JSON with errors (and by errors I mean that the JSON can be parsed as object yet there are some thing you want to change in that object) you could try to add "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;
            }
        };
    });
})

Note that you could also Overriding the Default Transformations

Also, make sure to also to:

   // 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/**'
    ]);
  }])

And if all went well you will be able to call:

//unment {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;
});

You can use douglascrockford's JSON-js.If it is not a valid json it will throw an error so that you can catch using try/catch and return a new Promise or simple true/false. If you don't use the library it will fallback to built in parser.

$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');
          }
      );

Default JSON parser behavior

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": "[email protected]","town", "london"}]';
var validJson = '[{"name": "anna","email": "[email protected]","town": "london"}]';
console.log("if invalid returns: ", parseJSON(inValidJson));
console.log("if valid get original object: ",parseJSON(validJson));

Short answer: no there isn't.

Long answer: If your JSON serialisation does not work in the backend, you have basically to parse a string an construct a new Object by yourself. There is no library which does that for you. And think of a different backend service.

I agree to @Florian's answer in general - there is no simple way of doing so.
I think that You should:

  • try to find a clever way to find the problem place;
  • replace the ma(s);
  • parse to JSON anew.

Idea of finding problem place: After every second (even) value there needs to be a ma. After every second (odd one - 1., 3., 5) - a colon. On each New { You have to start a new count. It probably is a pain in the ass, but doable.

It's possible, but cumbersome. For reasons It-Z went into, generally the best practice would be to fix the JSON being served by the API or to find a new API to work with. Assuming you have your reasons for not doing that, here is the logic flow:

  • catch the error from JSON.parse and feed it to a new function, along with the unparsed response string
  • in that new function
    • if you know you're only going to have this one case for your error, create logic to find and fix it; regex will be your friend here
    • if you're trying to catch multiple types of syntax problems, you'll need more plex regex and much more plex logic
    • pass the corrected string back to the original JSON.parse function

本文标签: javascriptHandle syntax error in JSON coming from APIStack Overflow