Parsing JSON Is a Minefield (2018)
seriot.ch
seriot.ch
Consider something as simple as parsing an integer in a text-based format; there may be whitespace to skip, an optional sign character, and then a loop to accumulate digits and convert them (itself a subtraction, multiply, and add), and there's still the questions of all the invalid cases and what they should do. In contrast, in a binary format, all that's required is to read the data, and the most complex thing which might be required is endianness conversion. Length-prefixed binary formats are almost trivial to parse, on par with reading a field from a struture.
I'll use JavaScript numeric literals here as my translation medium (ironic!):
Norway locale parses it to: 1001
USA locale parses it to: 1.001
France locale parses it to: NaN
https://docs.oracle.com/cd/E19455-01/806-0169/overview-9/ind...
I and you can agree that the reasonable thing to do is to accept American encoding.
(The below is somewhat simplified, the way I remember it on a late Saturday night 10+ years later.)
The outsourced team of programmers from our software vendor did not. They (mostly) used the built in regional settings in Windows (best practice, don't reinvent the wheel) meaning we had to come up with ways to make sure the machines ran with wrong regional settings (since a lot of stuff was already serialized that way && critical parts of the software was hardcoded to use US standard.)
Fun times ;-)
Do you just go with what the browser or client locale says? (User lives in Canada but their laptop is set to Turkish locale).
Go with the locale tied to the geo location of the user instead? (User above stubbornly enters all amounts using Norwegian conventions after completing high school in Norway).
If it's important, a confirmation page presented to the user and formatted in their presumed locale can help a lot.
> Length-prefixed binary formats are almost trivial to parse
They definitely are not, as displayed by the fact that binary lengths are the root cause of a huge number of security flaws. JSON mostly avoids that.
> the most complex thing which might be required is endianness conversion
That is a gross simplification. When you look at the details of binary representations, things get complex, and you end up with corner cases.
Let's look at floating point numbers: with a binary format you can transmit NaN, Infinity, -infinity, and -0. You can also create two NaN numbers that do not have the same binary representation. You have to choose single or double precision (maybe a benefit, not always). Etc.
Similarly in JSON integers or arrays of integers are nothing special. It is mostly a benefit not to have to specify UInt8Array.
JSON is one of many competitors within an ecology of programming, including binary formats, and yet JSON currently dominates large parts of that ecology. So far a binary format mutation hasn't beaten JSON, which is telling since binary had the early advantage (well: binary definitely wins in parts of the ecology, just as JSON wins in other parts).
I assume you're mainly referring to buffer overflows, which are a problem with text based formats too. See for example the series of overflow vulnerabilities in IIS's HTTP parser which lead to some of the most disruptive worms in history like Code Red. Really this is more of a problem with memory-unsafe languages than serialization formats.
> Let's look at floating point numbers: with a binary format you can transmit NaN, Infinity, -infinity, and -0
Depending on the use case being able to encode these values may be a requirement, in which case binary is no worse than text.
> You can also create two NaN numbers that do not have the same binary representation.
This is specific to IEEE 754, not all binary representations have this issue. Text based formats also have far more pervasive problems with lacking a canonical representation so it's hard to count this as a point against binary.
> JSON is one of many competitors within an ecology of programming, including binary formats, and yet JSON currently dominates large parts of that ecology.
This is just an appeal to popularity fallacy.
That is the most visible security issue, but there are many others e.g. reading excess data is a security flaw (a la heartbleed).
> Really this is more of a problem with memory-unsafe languages than serialization formats
JSON is often used to communicate with unsafe languages. Are you suggesting a binary format is better for using with a language that is memory-unsafe? Or are your implying we should use Rust so that we can use a binary format!
> This is specific to IEEE 754, not all binary representations have this issue.
So now we use some other (unspecified) binary format for floating point numbers?
> This is just an appeal to popularity fallacy.
Bullshit. JSON didn't become popular because it was popular. Developers have chosen to use JSON because it served a purpose for them within their particular ecology. It became popular into the headwinds of XML and other formats.
There are plenty of unpopular binary formats that developers have had experience with that they choose not to use. I have personally have enough experience with a variety of spec and custom binary formats to know when I would use one.
Binary formats most definitely have their place (embedded, high throughput at scale, severe bandwidth restrictions, strict typing, languages with poor string handling, naturally binary data). But for a large percentage of software projects, JSON works and it works well.
These are also easier to recover the data from in case of corruption, or re-sync the receiver, since you have a control byte you can sync on.
decompression and PNG libraries for example have caused massive security impact across the industry because of reuse in different products. Font handling, compressed bitmap, and windows cursor parsing also have been sources of issues.
Mozilla didn't just invest in Rust because parsing HTML and JSON are hard. Its all hard.
And then you go on to give examples of said scenarios of how this is true while saying I’m wrong? Anytime you have an unknown payload you have to make a determination of how long you’re going to wait, how much you’re going to accept, buffer, etc before it’s become a drain on the system
XML became popular because of its human readability compared to binary and allowed disparate systems to cooperate. JSON is even more readable and allowed the whole XLST thing to be ignored, making a dev's life easier, and really took off with node.
Binary is not gone, but you don't see a lot of it in the 'web world', because everyone in that space is JSON, almost exclusively.
Having written many parsers, binary is by far and away the easiest: provided your language/environment supports it. That is, binary in js is a pain, because you don't have native ints and floats. In js, JSON is the base atomic data structure, so it makes complete sense that it's used... But in c/c++ and co. JSON is harder and introduces a lot of overhead/ quirks which simply don't exist in binary (provided you cover the buffer overruns and co.)
XML was simply never designed to represent structured data. It was meant to represent document markup in a way that was both simpler and more extensible than SGML.
If there's one thing the industry always seems to do, its embrace some technology hammer as the solution for every problem. Before XML some people were trying to trade around relational database dumps as data interchange formats, because relational databases were the golden hammer.
JSON is being abused by being stretched beyond its sweet spot as well, but there aren't industry consortiums necessarily pushing bad ideas like there was with SOAP and WS-*.
If you're missing anything out of WS-* in JSON, you can be sure somebody is working on a spec for it.
Also sometimes it is a feature. The payload of a NaN value can be user defined and some programs use it[1]. The string "NaN" drops information that might be usefull to some programs, it just doesn't affect as many as null -> "null" does.
[1] https://github.com/WebKit/webkit/blob/master/Source/JavaScri...
Boy you are really stretching to make this sound complicated. It's not. You transmit 4 bytes or 8 bytes. Serialization is a memcpy().
You don't have to think about NaNs and Infinities because they Just Work -- unlike with textual formats where you need to have special representations for them and you have to worry about whether you are possibly losing data by dropping those NaN bits. If you want to drop the NaN bits in a binary format, it's another one-liner to do so.
It's funny that you choose to pick on floating-point numbers here, because converting floating-points to decimal text and back is insanely complicated. One of the best-known implementations of converting FP to text is dtoa(), based on the paper (yes, a whole paper) called "How to Print Floating-Point Numbers Accurately". Here's the code:
http://www.netlib.org/fp/dtoa.c
Go take a look. I'll wait.
dtoa() is not even the state of the art anymore. Just in the last few years there have been significant advances, e.g. Grisu2, Grisu3, and Dragon4...
Again, in binary formats, all that is replaced by a memcpy() of 4 or 8 bytes.
(A previous rant of mine on this subject: https://news.ycombinator.com/item?id=17277560 )
> > Length-prefixed binary formats are almost trivial to parse
> They definitely are not, as displayed by the fact that binary lengths are the root cause of a huge number of security flaws. JSON mostly avoids that.
Injection (forgetting to escape embedded text) is the root cause of a huge number of security flaws for text formats. Length-prefixed formats do not suffer from this.
What "huge number of security flaws" are you referring to that affect length-delimited values? Buffer overflows? Those aren't caused by values being length-delimited, they are caused by people deserializing variable-length values into fixed-length buffers without a check. That mistake can be made just as easily with a text format as with a binary format. In fact I've seen it much more often with text.
> JSON currently dominates large parts of that ecology.
JSON wins for one simple reason: it's easy for human developers to think about, because they can see what it looks like. This is very comforting. It's wasteful and full of pitfalls ("oops, my int64 silently lost a bunch of bits because JSON numbers are all floating point"), but comforting. Even I find it comforting.
Ironically, writing a full JSON parser from scratch is much more complicated that writing a full Protobuf parser. But developers are more comfortable with the parser being a black box than with the data format itself being a black box. ¯\_(ツ)_/¯
(Disclosure: I am the author of Protobuf v2 and Cap'n Proto. In addition to binary native formats, both have text-based alternate formats for which I wrote parsers and serializers, and I've also written a few JSON parsers in my time...)
Especially since text can be arbitrarily long. From that perspective, length-delimited text (I've seen that before in a few file formats, and more notably HTTP) is probably the worst of both worlds.
> more comfortable with the parser being a black box
they're more comfortable with the parser being a black box but the format being relatively easy to parse compared to the parser being easy to understand but the format basically unreadable for a human.
The symbol parsing part of the human brain is what parses letters and numbers, as well as other abstract symbols. The division of symbols into letters, numbers, and others is fairly arbitrary. Most people would say “&”, but the modern name of that symbol is a smoothing over of the way it was recited when it was considered part of the alphabet and recited with it.
> I suspect this phenomenon can be observed in the case of relative popularity of JSON, TOML, YAML and Python plus the relative unpopularity of Lisp, Haskell, Rust, XML.
I suspect not: Lisp and Haskell have less use of non-alphanumeric characters than most more-popular general purpose languages, and not significantly more than Python; also, if this was the phenomenon in play, popularity would be TOML > YAML > JSON but in reality it's closer to the reverse.
I really don't think that's true when you talk about about someone using the latin alphabet, words in that alphabet compared to some other alphabet (e.g. {}():!) and "words" (or meanings) in those. Just as a crude example parsing "c = a - b", where equals and minus are one symbol each and have been taught for a while, is different from parsing "c := a << b" where ":=" and "<<" basically act as a separate meaning someone has to learn to understand. Similar to the difference of latin alphabet and say simplified Chinese.
> also, if this was the phenomenon in play, popularity would be TOML > YAML > JSON but in reality it's closer to the reverse.
There could be somewhat of an sigmoid response to the effect, decreased reaction if you go into either extreme compared to deviating from the average.
I'm not a linguist so it is my speculation, so don't take it too seriously :D
JSON is perfectly capable of representing integers which cannot be represented in IEEE-754 double precision floating point. That seems at least a little special to me.
Way worse than that. The interpretation of those different values of NaN is software specific. You can also have signalling NaN values - where the recipient can now have their number handling code trap in completely unexpected scenarios.
With sufficient discipline and rigor, and a good suite of tests, developed over years of practical experience, you can evolve a good general binary wire protocol, but by then it will turn out to be so complicated and heavyweight to use, that some upstart will come up with a NEW FANTASTIC format that doesn't have any of the particular annoyances of your rigorous protocol, and developers will flock to this innovative and efficient new format because it will help them get stuff done much faster, and most of them won't run into the edge cases the new format doesn't cover for years, and then some of them will write articles like this one and comments like yours and we can repeat the cycle every 10-20 years, just like we've been doing.
^[ *][-?][0-9][0-9]*[ *]$
You're welcome. Anything that passes that regex is a valid number. Now using that as a basis of a lexer means that you can store any int in whatever precision you feel like.It's unfortunate that the majority of programmers these days are so computer illiterate that they can't write a parser for matching parens and call you an elitist for pointing out this is something anyone with a year of programming should be able to do in their sleep.
And it will still be a faster operation than fetching the next json file from disk by an order of magnitude, or two.
I had to write my own JSON parser/formatter a year ago (to support Java 1.2 - don't ask) and this article and its supporting github repo (https://github.com/nst/JSONTestSuite) was an unexpected gift from the heavens.
Doesn’t the test suite’s matrix demonstrate that there are tons of cases that aren’t handled consistently across these parsers?
If you're emitting JSON you can skim the list and avoid all of them.
Either way the minefield proper is no longer your problem.
So it certainly helped me. And just based on how thorough and insane the test suite is, I think I'm in good hands. Not perfect hands - but definitely a million times better than anything I would have come up with on my own.
The test suite made my parser blow up many times, and for each blow up I got to make a conscious decision in my bugfix: how do I want to handle this?
(I decided to let the 10,000 depth nested {{{{{{{{{{{{{{{{{{"key","value"}}}}}}}}}}}}}}}}}}} guy blow up even though it is legal. Yes, I'm too lazy to implement my own stack.) :-)
Between its strong schema and wsdl support for internet standards like soap web services, XML covers a lot of ground that Json encoding doesn't necessarily have without add-ons.
I say this knowing this is an unfashionable opinion and XML has its own weaknesses, but in the spirit of using web standards and LoC approved "archivable formats", IMO there is still a place for XML in many serialization strategies around the computing landscape.
Json is perfect for serializing between client and server operations or in progressive web apps running in JavaScript. It is quite serviceable in other places as well such as microservice REST APIs, but in other areas of the landscape like middleware, database record excerpts, desktop settings, data transfer files, Json is not much better or sometimes even slightly worse than XML.
If parsing JSON is bad, XML is a clusterfuck.
where was it insinuated XML is superior? It was a very reasonable response.
I still miss XSD and WSDL for a lot of cases. Other cases it was serious overkill where JSON is a better option.
XML isn’t superior. It’s heavier but more complete.
JSON is lighter and less complete.
Everything in code is always about trade offs. The error comes when people advocate for one solution all the time.
JSON is great because you don't need tooling. XML is great because it's expressive. You don't need expressiveness for a data format, but it works great as a markup language.
JSON can do that. It also maps pretty seamlessly to types/classes in most languages without annotations, attributes, or other serialization guides.
It also has explicit indicators for lists vs subdocuments vs values for keys, which xml does not. XML tags can repeat, can have subtags, and then there are tag attributes. A JSON document can also be a list, while XML documents must be a tree with a root document.
XML may be acceptable for documents. But seeing as how XHTML was a complete dud, I doubt it is useful even for that.
And we didn't even need to get into the needless complexity of validation, namespaces, and other junk.
But I think people just got sick of XML because it was abused so badly with "web services", SOAP, wsdl and all those horrible technologies from the early naughts. Over-complicated balls of mud that made people miserable.
Everyone abused XML some way or another. JSON is not that "abusable" I'd say.
XML is a really bad interchange format. It's OK for a document markup language, and that's where it survives.
So, that’s why we’re adding all of this “junk” back into JSON? Transformers, XPath for JSON, validation, schemas, namespaces (JSON-LD, JSON prefixes) it’s all there.
History repeating itself (and here’s the important part) because this complexity is needed. Not every application will need every complication, but every complication is needed by some application.
Unless you need to use the feature, you don't need to know anything about it, which is a huge benefit for the majority. XML almost encourages programmers to use unnecessary features.
When an application domain chooses to add a feature (say JSON-LD) then there are advantages to that mixture over XML. Where XML is better, it is often chosen instead.
Neither did XML. They simply took advantage of an early "processing directive" feature to add them in. XML and JSON are no different in this regard.
https://json5.org/ Jsonnet https://www.npmjs.com/package/comment-json
> Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser.
(Although I do love JSON5.)
XML has all that tooling because it needs it. JSON is a lot more straightforward, is more compact, and is faster to parse and (probably) generate.
If you're going to go through the effort of a compile step, you should probably just use a binary protocol, which will get you even better performance and getting documentation out of the box (e.g. protocol buffers schemas are very readable).
I see absolutely no reason to use XML these days as a data format, but it's still a response choice as a markup format (you know, what the M stands for).
What about cross-language? In C# I define a class containing a `DateTime` field, export the schema with xsd, and generate classes for Java with xjc, and get back a field of (an equivalent of) `DateTime` type. Doing what you suggest with JSON, I'd get a "string". Thanks but no thanks.
> If you're going to go through the effort of a compile step, you should probably just use a binary protocol, […] I see absolutely no reason to use XML these days as a data format,
In our product we use a relational db (SQLServer) combined with XML. Each table has a structured part which is put into relational columns, plus an extensions part that is put into a "Data" XML column for semi-structured data. SQLServer supports XQuery so we can query the semi-structured data from SQL when needed.
This wouldn't fly with a binary format.
EDIT: yes, SQLServer also supports JSON, but has special optimizations for XML (e.g., it can understand schema types, it supports XML indexes which "shred" XML to a more efficient binary representation based on schema, etc.)
? Using XML without a schema is slightly worse than JSON because the content of each node is just "text". XML with schema is far more powerful, also because of a richer type-system. JSON dictionaries are most of the time used to encode structs, but for that you have `complexType` and `sequence` in the XML schema.
I've been using XML with strongly-typed schemas for serialization for the last couple of years and couldn't be happier. I have ~100 classes in the schema, yet I've needed a true dictionary like 2 or 3 times.
> And we didn't even need to get into the needless complexity of validation, namespaces, and other junk.
Validation is junk? Isn't it valuable to know that 1) if your schema requires a certain element, and 2) if the document has passed validation, then navigating to that element and parsing it according to its schema type won't throw a run-time exception?
Namespaces are junk? They serve the same purpose as in programming languages. How else would you put two elements of the same name but of different semantics (coming from different sources) into the same document? You can fake this in JSON "by convention", but in XML it's standardized.
WSDL, SOAP, and all their precursors were attempted in spite of Postel's Law.
Repeating myself:
Back when I was doing electronic medical records, my two-person team ran circles around our (much larger) partners by abandoning the schema tool stack. We were able to detect, debug, correct interchange problems and deploy fixes in near realtime. Whereas our partners would take days.
Just "screen scrap" inbound messages, use templates to generate outbound messages.
I'd dummy up working payloads using tools like SoapUI. Convert those known good "reference" payloads into templates. (At the time, I preferred Velocity.) Version every thing. To troubleshoot, rerun the reference messages, diff the captured results. Massage until working.
Our partners, and everyone I've told since, just couldn't grok this approach. No, no, no, we need schemas, code generators, etc.
There's a separate HN post about Square using DSLs to implement OpenAPI endpoints. That's maybe 1/4th of the way to our own home made solution.
As AtlasBarfed mentioned, JSON has a native map and list structure in its syntax, which is sorely missed in XML. You have to rely on an XML Schema to know that some tag is expected to represent a map or list.
JSON with attributes and namespaces would be my ideal world.
Use JSON or a binary protocol for data, XML for markup.
You'd basically have to create a new independent format to have proper compatibility once you introduce breaking changes.
- remove DTDs completely.
- by removing DTDs, remove non-standard entities
- by removing DTDs, remove the concepts of notations and all external resource resolution from the core spec. Also, no possibility of entity-expansion attacks.
- by removing DTDs, remove validation from the core spec.
- merge namespaces into the core specification. At the same time, make them mandatory
- merge the concept of qualified names into the core specification
- by making namespaces mandatory, all the variations of how namespaces get exposed can be eliminated
- merge the info-set definition into the core specification
- by describing XML items and how they relate, implementations can understand what data is relevant at a particular point while parsing the document.
- Merge xml:id into the core specification.
You also had some other fun outlier concepts:
- Eliminate prefixes from infoset. This is mostly a breaking change for XPath and XML Schema.
- Add an explicit qualified name token (possibly recycling the entity declaration). This would allow the above specs to have their functionality restored, although likely with a new format.
- Accept qualified names without prefixes, such as via a {uri}:{localName} syntax.
JSON is exactly designed for object serialization. XML can be used for that purpose but it's awkward and requires a lot of unnecessary decisions (what becomes a tag? what becomes an attribute? how do you represent null separately from the empty string?) which just have an easy answer in JSON. And I can't think of any advantage XML has to make up for that flaw. Sure, XML can have schemas, but so can JSON.
I will agree that JSON is horrible for config files for humans to edit, but XML is quite possibly even worse at that. I don't really like YAML, either. TOML isn't bad, but I actually rather like JSON5 for config files - it's very readable for everyone who can read JSON, and fixes all the design decisions making it hard for humans to read and edit.
true
false
null
0 | 1
"true" | "false" (with assorted variation by
"yes" | "no" case and initial character)
"" | "0" | "1"
"\u2713" (hi DHH)
-1 (with complements)
"[object Object]"
{ "value": true } (and friends)
(attribute not present)
"敵牴"
That last looks like a doozy, but old lags will guess what's going on right away. It's the octets of the 8-bit string "true", misinterpreted as UCS-2 (16-bit wide character) code points and then spat out as UTF8. Google translates it, quite appropriately, as "Enemy".Oddly though, according to my records, never seen a "NULL".
Really, the only time it would matter is if you are parsing user-provided JSON and said user was trying to exploit your parser somehow.
But 99% of the time, I'm not parsing user-provided JSON, so I don't ever encounter these corner cases and parsing/serialization works great.
I take it you've never implemented a service with a REST API.
I meant to say: "I have, but I work mainly with embedded systems, and so there aren't usually POST or PUT APIs, just GET. So JSON is nearly always generated by the device and consumed by whoever is querying the device, not the other way around."
Consider this JSON: {"key": 9223372036854775807}. With most parsers it never fails.
But... some JSON parsers (include JS.eval) parse it to 9223372036854776000 and continue on their merry way.
The problem isn't user-provided JSON here. The problem is user-provided data (or computer-provided data) that's inside the JSON.
rachelbythebay's take (http://rachelbythebay.com/w/2019/07/21/reliability/):
On the other hand, if you only need 53 bits of your 64 bit numbers, and enjoy blowing CPU on ridiculously inefficient marshaling and unmarshaling steps, hey, it's your funeral.
- I sometimes use random 64 bit integers in my data (usually as cheaper synthetic guid-like keys).
- I sometimes use CRC-64 hashes.
- I sometimes use 2^63 as a sentinel.
For an made-up example of how this could get you, let’s imagine that you have a message service that gives each message a unique ID. Some bright soul decided to give this ID a nice structure and make it a 64-bit value where the top 32 bits are an incrementing integer per user, and the bottom 32 bits are the user’s ID, assigned by with a global incrementing integer.
Everything works great in testing and you deploy and the VC money is rolling in and then some of your very prolific users go past 2 million messages and suddenly messages are getting mixed up and you’re leaking private info because your access checking code happens to get the real 64-bit value but your message retrieval code puts the ID in a JSON number.
Now you might respond, but that ID scheme is dumb, don’t do that. And you may be right! But dumb things happen. It’s unwise to leave land mines lying around in your software just because they only detonate when someone does something dumb.
Or, your server keeps track of how long it's been running, in nanoseconds. When the server stays up for months, people start noticing weird precision issues and hard to replicate bugs around time.
This is correct behavior though...? Every number in JSON is implicitly a double-precision float. JSON doesn’t distinguish other number types.
If you want that big a string of digits in JSON, put it in a string.
Edit: let me make a more precise statement since several people seem to have a problem with the one above:
Every number that you send to a typical JavaScript JSON parser is implicitly a double-precision float, and it is correct behavior for a JavaScript JSON parser to treat a long string of digits as a double-precision float, even if that results in lost precision.
The JSON specification itself punts on the precise semantic meaning of numbers, leaving it up to producers and consumers of the JSON to coordinate their number interpretation.
> This specification allows implementations to set limits on the range and precision of numbers accepted. Since software that implements IEEE 754 binary64 (double precision) numbers [IEEE754] is generally available and widely used, good interoperability can be achieved by implementations that expect no more precision or range than these provide, in the sense that implementations will approximate JSON numbers within the expected precision. A JSON number such as 1E400 or 3.141592653589793238462643383279 may indicate potential interoperability problems, since it suggests that the software that created it expects receiving software to have greater capabilities for numeric magnitude and precision than is widely available.
> Note that when such software is used, numbers that are integers and are in the range [-(2^53)+1, (2^53)-1] are interoperable in the sense that implementations will agree exactly on their numeric values.
Or to paraphrase: if you pretend that every JSON number is a JavaScript number (double-precision float), you will generally be fine. If you don’t, you are responsible for the inevitable interpoperability problems you’ll have with most current JSON parsers.
Is it? I was under the impression every number in JSON is implicitly arbitrary precision.
If you control both the producer and consumer of the serialized data, you can of course do whatever you like. But I would recommend people who want more extensive data types use something other than JSON.
Don't forget all the unintended intermediate producers and consumers due to microservices or even otherwise well-written tools that convert to float64 internally.
Is that a long or an int? Boolean or the string "true"? Does my library include undefined properties in the JSON? How should I encode and decode this binary blob?
We tried using OpenApi specs on the server and generators to build the clients. In general, the generators are buggy as hell. We eventually gave up as about 1/4 of the endpoints generated directly from our server code didn't work. One look at a spec doc will tell you the complexity is just too high.
We are moving to gRPC. It just works, and takes all the fiddling out of HTTP. It saves us from dev slap fights over stupid cruft like whether an endpoint should be PUT or POST. And saves us a massive amount of time making all those decisions.
Ref link:- https://v8.dev/blog/v8-release-76
JSON.parse(Array(3000).join('[')+Array(3000).join(']'))
We were about to report a bug when we noticed that the problem was fixed in Chrome 76, and the users in question were still on Chrome 75.https://github.com/lemire/simdjson
I’ve been using to process and maintain giant JSON structures and it’s faster than any other parser I’ve tried. I was able to replace my previous batch job with this as it gives real-time performance.
We haven't used that particular suite, but almost everything in that suite is something we've thought about. In many cases we do the right thing by not innovating and randomly allowing stuff that isn't in the spec.
I see exactly one thing we didn't think about, as our construction of a parse tree is pretty basic and we don't build an associative structure even when building up an object - thus we would not register an error when confronted with the malformed input listed under "2.4 Objects Duplicated Keys", but happily build a parse tree with duplicated keys (which will be built up strictly as a linear structure, not an associative one).
There seems to be leeway on this point as to what an implementation should do. It certainly doesn't fit our usage model very well to build a associative structure right there on the spot - some of our users wouldn't want that much complexity/overhead.
> Abstract Syntax Notation One (ASN.1) is a standard interface description language for defining data structures that can be serialized and deserialized in a cross-platform way. It is broadly used in telecommunications and computer networking, and especially in cryptography.
https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
Or keep re-inventing the wheel. It's not like the people paying you will notice or care, eh?
Protobuf and friends have most of the power without a lot of the drawbacks.
For whom?
> There are dozens of cases of security bugs based on bad parsers.
You're saying this on a thread called "Parsing JSON Is a Minefield", eh?
In any event, this is not unique to ASN.1. I haven't checked but I don't doubt there are similar cases for Protobuf, etc.
> Also there are a dozen different encodings of asn.1 data including json (JER).
So what? That's the opposite of a problem.
> Its age also means that it has a bunch of obsolete datatypes.
So don't use them.
- - - -
My point is that if the time and effort that was spent on Protobuf and CapnProto and all the others had somehow been spent instead on perfecting ASN.1 then, uh, that would have been good...
I wrote proto2 in 20% time at Google and I developed Cap'n Proto entirely on my own time, unpaid. If you think ASN.1 could be perfected with a similar amount of work then why don't you do it?
I'd love to discuss this but don't want to get in a flame war.
In re: ASN.1, if I ever have to de/serialize some messages again (I'm quasi-retired ATM) I would use ASN1SCC "an ASN.1 compiler that was developed for ESA to cover all data modelling needs of space applications."
> The compiler is targetting safe systems and generate either Spark/Ada or C code. Runtime library is minimalistic and open-source. The tool handles custom binary encoding layouts, is fully customizable through a code templating engine, generates ICDs and automatic test cases."
https://essr.esa.int/project/asn1scc-asn-1-space-certifiable...
But this comment seems needlessly cynical and doesn't actually offer any rebuttal to the parent's point. I find technical discussions of these sorts of things interesting and a great way for new people to learn about the tradeoffs, maybe you could offer a reasoned opinion on why not use protocol buffers?
One should keep schema in protocol buffers and encourage its use for all but browsers.
This severely limits its usefulness, unless you want to put workarounds of this shortcoming into your application.
Other protobuf-like things like capnproto don't have that restriction, because they don't use 32 bit integers for sizes.
Also even in json, splitting documents in smaller chunks (ndjson for instance) is the standard practice to avoid having to parse it all in one go.
Cap'n Proto is different, since it's zero-copy and random-access. You can in fact read one bit of data out of a large file in O(1) time by mmap()ing it and using the data structure in-place.
Hence, it makes sense for Cap'n Proto to support much larger messages, but it never made sense for Protobuf to try.
Incidentally the 32-bit limitation on Protobuf is an implementation issue, not fundamental to the format. It's likely some Protobuf implementations do not have this limitation.
(Disclosure: I'm the author of Protobuf v2 and Cap'n Proto.)
If I want to perform some rough tests of an endpoint during development, all I need to do is compose the json request and fire it off using curl. The response then comes back in a human readable format I can parse straight from the terminal. Boom, simple test conducted in less than 1 minute. I don't even need to think about it.
Compare that to protobufs; I need to create a custom client or unit test that'll compose and fire off the request I want to test, then I need to write a bunch of code that will introspect the contents of the response so I can pick out the details. Huge time loss, concentration ruined since I need to actually think about the process, I'd rather just take the extra latency that using json will incur.
This skips past all of the other advantages json has over binary serialization protocols, like quickly being able to parse requests while debugging issues, infinite client language support, ease of sharing breaking requests to help devs reproduce problems, not needing to add an extra compilation step to my deployments and packages, etc.
With things like Avro or GPB you still need to validate that the relationship holds true separately.
In case anyone doesn't click the link, its much simpler than JSON, leaves interpretation up to the reader, supports polymorphic data, and has some tweaks to make it nicer to edit by hand. I've used this in a bunch of personal projects and maps all the models I've come across perfectly. If you need more power in your format you're better off using Lua than YAML.
I have a Rust Serde implementation 90% complete I could finish up if anyone wants it.
This doesn't seem simpler than JSON otherwise, though, e.g. type declarations and optional quotes.
Why is leaving interpretation up to the reader desirable? Shouldn't things always come out the same?
Asterisks are an unusual choice of comment syntax. What if your comment needs to contain an asterisk? Why not "//..." and/or "/* ... */", or "#..."?
Something like https://github.com/nst/JSONTestSuite? Could a parser test suite be an official component of an RFC like 8259?
What am I missing, when do these gotchas become an actual problem for you as a developer?
I’m not sure I’ve used any technology that was free of footguns, and JSON appears to have fewer of them than the average programming language or library.
> when do these gotchas become an actual problem for you as a developer?
Whenever you have to deal with JSON produced by “not you”, or when you have to deal with JSON that may have been corrupted in some fashion along the way.
What industry do you work in? I don’t see many systems like that in my industry. I work like hell to push things in that direction, but it’s a best case of “we went from 5% well defined behavior to 50%” after many years of effort.
If you pick up a standard and just assume other products will be able to work with it, you're in for a surprise. I don't care if it's TCP sockets or .ini files; if you didn't test compatibility with the product you expect will interact with yours through the standard, consider it unstable, and don't advertise support for it.
Sometimes you have to support a standard itself, like WPA2, so you implement the standard according to internet engineering best practice: be liberal with what you accept, and conservative with what you transmit (or something to that effect). Then test compatibility with the major products you know will want to use it, and fix the bugs you find.
String formats are great an all for viewing in whatever text viewer but so inefficient and then you have the whole escaping string inside of strings and string encoding binary data.
If we all agreed on a binary format then there would be a viewer for it in every debugging tool.
ASN.1, Protobuf, BSON, ION. MSGPACK whatever. I would prefer a binary format that doesn't repeat keys for efficiency where the schema can be sent separately or inlined. But even one that's basically binary JSON with more types would be step up.
Anyway, given the choice, I always pick JSON over XML. Because with JSON, I can always identify the data blocks that I need, and parse them out with bash and spreadsheets. Not with XML, however. Just as not with HTML.
FWIW, this appears to have been fixed recently.
XML and JSON last I checked.
Its various spec bugs (by omission) are not that dramatic, and the various "enhancements" only made it worse, ie more insecure. Still, bad but not a minefield. What worries me most that my JSON module is the defacto perl standard, passes all these tests, was the very first to add all these tests, is the fastest, and still is not included in that list, just some outdated modules which should not be used at all. Checking best practices besides maintaining a spec obviously also is a minefield.
This is in fact the most important problem to test against, because it might lead to exploitable stack ROP.
JSON is usually parsed recursively, and deeply nested structures are mostly not depth counted. One can trivially construct a nested array or map of 500 to 30000 elements, and at one point the parser either fails or crashes with an overflow. This number is fixed, thus trivially exploitable. The test spec should contain the max. depth for arrays and maps, and if there's a fixed builtin limit, a compile-time limit, implicit limit by crash, or none. non recursive parsers are fine.