Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I validate JSON in Perl?

Tags:

json

perl

I am familiar with the decode_json() function which attempts to decode a JSON string, and terminates the Perl program with error if it fails to do so—but this question is not about that function.

What I want to do is this—I want a function through which I can run a JSON string and the function will simply tell me whether or not the JSON string is kosher—and if it isn't kosher, give me detailed information what is wrong with it.

Is there a function like this? If so, can someone here just point me in the right direction?

The function does not need to decode the JSON string—it just needs to inform the calling program if the JSON string can be decoded—and allow the calling program to continue it's run regardless of the outcome.

If something is wrong with the string - I need to know more than just what kind of error it is, but where in the string that error is—including what line number if the string is to be interpreted as the full-contents of a file. In short, information that is useful for debugging the JSON file that the string was read from.

like image 440
Sophia_ES Avatar asked Dec 26 '22 02:12

Sophia_ES


1 Answers

decode_json() croaks on error. You can catch that error by wrapping in an eval or using Try::Tiny, if you need to use the data programmatically. The croak error message shows where the json parser failed.

$perl_scalar = decode_json $json_text

The opposite of encode_json: expects an UTF-8 (binary) string and tries to parse that as an UTF-8 encoded JSON text, returning the resulting reference. Croaks on error.

http://search.cpan.org/~mlehmann/JSON-XS-3.01/XS.pm#FEATURES

#!/usr/bin/env perl

use JSON qw(decode_json);

my $maybe_json = '{"foo":"bar", "baz"}';  # invalid, baz key is missing value
my $json_out = eval { decode_json($maybe_json) };
if ($@)
{
    print "decode_json failed, invalid json. error:$@\n";
}

produces:

decode_json failed, invalid json. error:':' expected, at character offset 19 (before "}") at ./foo.pl line 6.

Deciphering the decode failure message can be frustratingly difficult.

This one is easy, because we know it is caused by a key without a value, so the parser complains that it didn't find the : it expected at char 19. It also adds the useful 'before "}"' message to help the human interpret the error.

It might be nice to have a way to automatically parse the error message and show a larger chunk of the input string.

Let's take a quick python diversion, does that json parser provide a more useful error? Nope, it just shows char 19 without even the "before }" context.

>>> import json
>>> maybe_json = '{"foo":"bar", "baz"}'
>>> json.loads(maybe_json)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/andrew/anaconda/lib/python2.7/json/__init__.py", line 338, in loads
    return _default_decoder.decode(s)
  File "/Users/andrew/anaconda/lib/python2.7/json/decoder.py", line 365, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/Users/andrew/anaconda/lib/python2.7/json/decoder.py", line 381, in raw_decode
    obj, end = self.scan_once(s, idx)
ValueError: Expecting : delimiter: line 1 column 20 (char 19)

So, what's the final answer? The json parser specification is pretty simple and in case of error just quits. Call the parser, if it succeeds the string was valid, else show the user where it died and ask what to do. Perl's json parser also allows options for a more relaxed translation, like interpreting single quotes (illegal) as double quotes (valid). See relaxed and allow_singlequote options.

Maybe you do want a linter?

like image 102
spazm Avatar answered Jan 24 '23 12:01

spazm