Why is JSON invalid if an integer begins with a leading zero?
Asked Answered
M

6

74

I'm importing some JSON files into my Parse.com project, and I keep getting the error "invalid key:value pair".

It states that there is an unexpected "8".

Here's an example of my JSON:

}
 "Manufacturer":"Manufacturer",
 "Model":"THIS IS A STRING",
 "Description":"",
 "ItemNumber":"Number12345",
 "UPC":083456789012,
 "Cost":"$0.00",
 "DealerPrice":" $0.00 ",
 "MSRP":" $0.00 ",
}

If I update the JSON by either removing the 0 from "UPC":083456789012, or converting it to "UPC":"083456789012", it becomes valid.

Can JSON really not accept an integer that begins with 0, or is there a way around the problem?

Microsecond answered 8/12, 2014 at 15:45 Comment(6)
JSON syntax doesn't allow numbers to start with the digit 0. You can of course put your numbers in quotes.Southernmost
A decimal number starting with a zero should be a stringFoetus
If you have a 0, then it's more than likely not an integer but a char identifier. Just treat them as strings!Loquacious
this isn't a decimal number, it's a UPC which is an integer that is assigned to a specific product model. @AlexK.Microsecond
Also, describe "UPC", we'll tell you if you need a string or an integer.Loquacious
It's syntactically illegal. Numbers starting with a zero must either be just the zero or a simple fraction. Have a look here: json.org/json-en.htmlVermiculate
C
92

A leading 0 indicates an octal number in JavaScript. An octal number cannot contain an 8; therefore, that number is invalid. Moreover, JSON doesn't (officially) support octal numbers, so formally the JSON is invalid, even if the number would not contain an 8. Some parsers do support it though, which may lead to some confusion. Other parsers will recognize it as an invalid sequence and will throw an error, although the exact explanation they give may differ.

Solution: If you have a number, don't ever store it with leading zeroes. If you have a value that needs to have a leading zero, don't treat it as a number, but as a string. Store it with quotes around it.

In this case, you've got a UPC which needs to be 12 digits long and may contain leading zeroes. I think the best way to store it is as a string.

It is debatable, though. If you treat it as a barcode, seeing the leading 0 as an integral part of it, then string makes sense. Other types of barcodes can even contain alphabetic characters.

On the other hand. A UPC is a number, and the fact that it's left-padded with zeroes to 12 digits could be seen as a display property. Actually, if you left-pad it to 13 digits by adding an extra 0, you've got an EAN code, because EAN is a superset of UPC.

If you have a monetary amount, you might display it as € 7.30, while you store it as 7.3, so it could also make sense to store a product code as a number.

But that decision is up to you. I can only advice you to use a string, which is my personal preference for these codes, and if you choose a number, then you'll have to remove the 0 to make it work.

Caterinacatering answered 8/12, 2014 at 15:47 Comment(10)
so any number that must begin with a "08" will always be invalid?Microsecond
@Microsecond It's the leading 0 that's the problem.Southernmost
So you can quote it "UPC":"083456789012", if it's an UPC barcode, you should not parse it as int.Amsterdam
Well, I definitely learned something new today. Thanks @GolezTrol.Microsecond
An error i got in django rest api for number with leadign zero is like this: { "detail": "JSON parse error - Expecting ',' delimiter: line 7 column 16 (char 125)" }Permeance
"A leading 0 indicates an octal number in JavaScript." That's incorrect, the specification has never had a leading 0 mean octal. It didn't forbid it (originally), and so sadly some JavaScript implementations decided to do it, but it was never standard and still isn't. In strict mode, it's not allowed. Octals in JavaScript (as of ES2015) start with 0o (case insensitive, analogous to 0x for hex).Enallage
@T.J.Crowder Good point. It was an optional extension and as such was mentioned in the standard, but that's semantics. It has indeed always been discouraged, and currently it's banned from the standard, so it's indeed best to avoid octal number both in Json and in JavaScript.Caterinacatering
@GolezTrol: Wow, you're absolutely right. Went back and checked, and both the 1st and 2nd editions of the ECMAScript spec list OctalIntegerLiteral, without any apparent hedging. My bad! The 3rd edition deprecates it, and the 5th edition introduces strict mode to remove it.Enallage
Adding a double quote explicitly to a number with leading zeros is not really a good solution.Sacken
@Sacken Yes it is. A number with a leading 0 is a formatted number and should therefore be treated as a string. Please note that the quotes are not part of the number, they are just there to indicate that the value is to be interpreted as a string and will be gone after deserialization. And it's simply not possible to store a number with leading numbers as an unquoted number, without making the Json invalid. So if you have a brilliant alternative that nobody thought of, please enlighten us, but otherwise, please be fair and remove your downvote, because this answer is simply how it is.Caterinacatering
P
11

One of the more confusing parts of JavaScript is that if a number starts with a 0 that isn't immediately followed by a ., it represents an octal, not a decimal.

JSON borrows from JavaScript syntax but avoids confusing features, so simply bans numbers with leading zeros (unless then are followed by a .) outright.

Even if this wasn't the case, there would be no reason to expect the 0 to still be in the number when it was parsed since 02 and 2 are just difference representations of the same number (if you force decimal).

If the leading zero is important to your data, then you probably have a string and not a number.

"UPC":"083456789012"

A product code is an identifier, not something you do maths with. It should be a string.

Pirog answered 8/12, 2014 at 15:49 Comment(0)
S
4

Formally, it is because JSON uses DecimalIntegerLiteral in its JSONNumber production:

JSONNumber ::
    -_opt DecimalIntegerLiteral JSONFraction_opt ExponentPart_opt

And DecimalIntegerLiteral may only start with 0 if it is 0:

DecimalIntegerLiteral ::
    0
    NonZeroDigit DecimalDigits_opt

The rationale behind is is probably:

  • In the JSON Grammar - to reuse constructs from the main ECMAScript grammar.
  • In the main ECMAScript grammar - to make it easier to distinguish DecimalIntegerLiteral from HexIntegerLiteral and OctalIntegerLiteral. OctalIntegerLiteral in the first place.

See this productions:

 HexIntegerLiteral ::
     0x HexDigit
     0X HexDigit
    HexIntegerLiteral HexDigit

...

OctalIntegerLiteral ::
    0 OctalDigit
    OctalIntegerLiteral OctalDigit
Seagraves answered 8/12, 2014 at 15:54 Comment(3)
"And DecimalIntegerLiteral may only start with 0 if it is 0", or has a decimal point straight after the 0 and some more digitsGerous
@Gerous This is not DecimalIntegerLiteral then.Seagraves
@Seagraves ah, yes, the JSONFraction_opt handles that bit. Read too quickly, sorry :)Gerous
U
2

The UPC should be in string format. For the future you may also get other type of UPC such as GS128 or string based product identification codes. Set your DB column to be string.

Underneath answered 8/12, 2014 at 16:10 Comment(0)
H
1

I think the easiest way to send your number by JSON is send your number as string.

Hillel answered 8/12, 2014 at 16:4 Comment(1)
It's not octal - it's a fixed-length code and should be treated as a stringGerous
R
0

If an integer start with 0 in JavaScript it is considered to be the Octal (base 8) value of the integer instead of the decimal (base 10) value. For example:

var a = 065; //Octal Value
var b = 53;  //Decimal Value
a == b; //true
Recourse answered 8/12, 2014 at 15:50 Comment(2)
Except that it's not valid at all in JSON... Try JSON.parse("65") vs JSON.parse("065")Gerous
maybe, just maybe if someone got confused : 065 => 0*8^2 + 6*8^1 + 5*8^0 = 53 (decimal)Millais

© 2022 - 2024 — McMap. All rights reserved.