json5 / json5-spec

The JSON5 Data Interchange Format
https://spec.json5.org
MIT License
49 stars 11 forks source link

Integers as keys in dictionaries #10

Closed willemt closed 4 years ago

willemt commented 5 years ago

In JSON keys MUST be strings. It would be handy if it was possible to use integers as keys like so:

{
  1: "foo",
  2: "bar"
}

Example usecase I have a REST service that represents assignments between objects like below. It would be better if the REST service could use integers instead of strings for keys.

{
    "assignments": {
        "2387": 35,
        "2477": 39,
        "2574": 30,
        "2575": 33,
        "2576": 37,
        "2645": null,
        "2741": 36,
        "2742": 32,
        "2743": 31,
        "2745": 31,
        "2746": 39,
        "2747": null,
        "2748": 32,
        "2819": 33,
        "2821": null,
        "2823": 38,
        "3038": 36
    }
}
jordanbtucker commented 5 years ago

Thanks for the suggestion, and thanks for opening this issue in the correct repository.

There was discussion on this a long time ago. At that time, we decided not to implement this feature. I'm not opposed to revisiting this idea, but I do have some reservations.

I've quoted some of the original discussion here as this is the proper place to continue discussion.


ES5 supports numeric literals as property names in object literals. [ES5 §11.1.5]

This allows productions like:

{    1 : 'int',
    .2 : 'float',
    3. : 'trailing dec',
  4e-2 : 'exp',
  0x50 : 'hex' }

All property names are normalized to strings. So the above will result in:

{    '1' : 'int',
     '3' : 'trailing dec',
    '80' : 'hex',
   '0.2' : 'float',
  '0.04' : 'exp' }

Note that the definition of numeric literal precludes signed numbers and special IEEE 754 values, so the following productions are either errors or not treated as numeric values.

{        -1 : 'neg (error)',
         +1 : 'pos (error)',
   Infinity : 'inf (valid but parsed as identifier)',
  -Infinity : 'neg inf (error)',
        NaN : 'not a num (valid but parsed as identifier)' }

Are there plans to support the same syntax that ES5 does? I don't see floating points used as property names very often, but I can see decimal and hexadecimal integers being useful.


I couldn't find any rationale in ES5 as to why this is allowed. I think it's more of a convenience thing. I can see where it could make your code cleaner, especially if you want to store a hash table using hexadecimal numbers as keys.

// ELF header
// Source: http://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_layout
// { offset : fieldName }
{ 0x00 : 'EI_MAG0',
  0x04 : 'EI_CLASS',
  0x05 : 'EI_DATA',
  0x06 : 'EI_VERSION',
  0x07 : 'EI_OSABI',
  0x08 : 'EI_ABIVERSION',
  ... }

But this brings with it at least two issues that come to mind:

  1. The user must be aware that the keys 1, 1., 1.0, 1.00, 1e0, 1e-0, 1e+0, 1.e0, 1.0e0, 0x1, 0x01, ad infinitum all get stored as '1' in the object, and as such will overwrite each other.
  2. JSON5.stringify must either:
    • parse keys to detect whether they are identifiers, numbers, or strings and output them appropriately, or
    • output numeric keys as strings.
      • In either case, the original representation of the key will be lost unless it conformed to the output of the ToString method as indicated at the bottom of ES5 §11.1.5

Another thing to consider is interoperability. In many languages, JSON objects are represented as dictionaries or associative arrays with string keys. However, those languages also support numeric keys.

JavaScript

const hash = {123: 'abc'}
hash[123]   // returns 'abc'
hash['123'] // returns 'abc'

Ruby

hash = {123 => 'abc'}
hash[123]   # returns 'abc'
hash['123'] # returns nil (Ruby's version of null)

Python

hash = {123: 'abc'}
hash[123]   # returns 'abc'
hash['123'] # throws KeyError

C#

var hash = new Dictionary<int, object> { { 123, "abc" } }
hash[123]   // returns "abc"
hash["123"] // compilation error

So how do these languages treat the JSON5 document {123:'abc'}? Is it a hash table with integer keys or string keys? If the keys are integers, then what happens when you mix key types?

{
  123: 'abc',
  'a wild string appears': "it's not very effective"
}

If the language treats all keys as strings, then you run into the issue of normalizing those numbers as strings.

{    1 : 'int',
    .2 : 'float',
    3. : 'trailing dec',
  4e-2 : 'exp',
  0x50 : 'hex' }

becomes

{    '1' : 'int',
   '0.2' : 'float',
     '3' : 'trailing dec',
  '0.04' : 'exp',
    '80' : 'hex' }

and the keys have to be converted back into numbers after they've been parsed if you actually wanted numeric keys.

mindplay-dk commented 4 years ago

ES5 supports numeric literals as property names in object literals. [...] All property names are normalized to strings

The conversion from numbers to strings is lossy.

While this may be convenient in JS at times, when it comes to a data storage/transport format, this could lead to some extremely unpredictable bugs.

In my optic, this sort of thing is unprecedented in JSON, which doesn't do any other type-conversions on the fly - I think that's one of the reasons JSON is very safe and reliable as a data format; probably also why implementations among platforms very rarely have bugs.

In my opinion, if (as a user of a JSON5 library) you wanted a "number-looking string" key, then use a string key - it's much safer for you to own the responsibility of converting your numbers to/from strings, as, for one, conversion facilities can behave very differently among platforms.

wongchichong commented 3 years ago

I think this is necessary, I need sparse array., because of this, I use json-z for the following syntax:

[.1, .1,,, .1, 10: .1, "100000": .1]

As the title is integers as key, those floating point name, engineering numeric should be ignore. JSON = Javascript Object Notation, other languages such as Ruby/C#/Python should comply to Javascript Object Notation behavior for interoperability .

AlexiyOrlov commented 3 years ago

Is a key with plus sign valid, like one+two: "three"?

jordanbtucker commented 3 years ago

Is a key with plus sign valid, like one+two: "three"?

No, it is not valid ES5 nor JSON5.