JSONItem not instantiating empty arrays correctly

Hmmm, my goal with the class is definitely not to encourage the designing of bad JSON generators. It was written for JSON consumption (more than JSON production) with two goals in mind:

  1. Consume well formed (good) JSON super fast.
  2. Sidestep the empty array issue. (a more recent goal)

Strict error checking is removed from the class to achieve the first goal of fast parsing. (You can even replace all colons in a JSON string with any other chracter, and the parser will still parse the JSON correctly.

I work with 3D assets stored as JSON strings, that sometimes have polygons and vertices in the range of millions. There objects needs to parsed fast, and the current JSONItem(data) constructor takes too long to parse such demanding data sets. (Was the first reason for writing a JSON parser in the first place)

Number 2 really is a showstopper for me at this point, and I have to get a solution in place that can sidestep it, because I’m on the brink of delivering my first Xojo written commercial product, and only have a few months left to launch date.

Any JSON passed to the class should already well-formed, because for the speed reasons describe above, very little error checking is done.

Given all this, when the empty array issue is resolved, I will switch back to the Xojo JSONItem object in an instant, since most of my projects do not require the accelerated parsing. I much rather prefer to stick to the built-in features of Xojo where possible instead of using custom components. On top of this, I will never recommend anyone to use my class unless speed and the empty array issue is a showstopper.

I’m hoping this class of mine will only be a short/medium term workaround for my own projects.

It is worth a lot… I never realized names without quotes implied actual objects… always thought it was just a shorthand to avoid typing quotes. Now I’m a little bit wiser on the topic :wink:

But then it’s no longer JSON, and that’s what I’m getting at. If you remove the strict checking, it means that a beginner using your class may “learn” to create JSON that doesn’t work anywhere else.

If you’re going to call it a JSON parser, it really should follow the spec: http://www.json.org. Otherwise call it something else.

I know the JSON spec very well.

The ToString() method of object the generates JSON 100% according to the specifications defined on the JSON website. The object also parses any string that is formatted according to the specifications on the website without error.

There is no doubt in my mind that this parser is a 100% legit JSON parser.

Question… surely the burden of badly generated JSON should fall on the generator of the JSON, and NOT on the parser of the JSON?

In my mind, it’s the responsibility of both parties. By allowing badly formed JSON, users may learn to create JSON strings incorrectly and then have trouble with other RFC compliant parsers later. For instance, if someone were just starting out learning JSON and your parser was the first one they used, unless your parser defaults to a JSON RFC compliant strict mode, there will be the possibility of them missing some obvious mistakes. Things like not putting quotes around the key, or accidentally using a semicolon as a delimiter instead of a colon. From what you’ve said, this: { name ; "Mickey" } would parse just fine, and I find that misleading.

Anyway… just my 2?

I get what you’re saying Greg… even though my class solves my problems… it is not a great example to give to beginners, and I have therefore removed the download.

I took the liberty of adding exception handling to the class, that mimics the current exceptions of the JSONItem class. The class now parses strictly according to the JSON specification, and any non-JSON strings raises an exception.

New download link.

The problem is now, with all the error checking added, the parsing is SIGNIFICANTLY slower than the parsing speed of native Xojo JSONItem. (In fact, it seems even without the error checking the native Xojo parser is faster… have you guys done any optimizations to the parser in the recent months???)

Like I said before, I wrote this class (hopefully) as a temporary solution for the empty array issue. I do realize that the Xojo team has a lot on their plates, especially with XDC coming, and all the hard work going into getting iOS and LLVM ready.

If they do get a chance to revisit this issue, I’m sure it will benefit everyone that relies heavily on JSON processing in their data applications.

Thanks for all your input Greg.

PS. Hopefully I can make a future Xojo event to meet everyone in person. I was planning to come to XDC, but my newborn boy of 5 weeks kinda put a hold on that idea.

Ok, so I have been racking my brain on this issue, since there HAS to be an easier way to bypass the empty array issue instead of writing a whole new parser.

After re-reading the workaround given in the feedback ticket, it is obvious that one should simply subclass the JSONItem and then override the Contructor and ToString() function to intercept “[]” objects.

I’ve updated the test project with these changes. The “workaround” object now runs just as fast as the native JSONItem object, and empty arrays are correctly returned as “[]” by ToString. A custom JSONItem Exception class is also no longer needed.

The download link has been updated with the new class.

…unfortunately a real workaround isn’t that simple. Finding one for top-level empty arrays is relatively easy, but try the following String through your test project {"empty":[],"filled":["element1","element2"]}
There are different ways to create an JSONItem: parsing String, direct JSONItem methods, transformation from Xojo data structures. Esp the latter is something that I rely on heavily. Empty arrays should be handled from and to JSON with all three methods.
IIRC you can create such a beast like above correctly by constructions like:

  dim json as JSONItem
  dim c as new Collection
  c.Add("element1")
  c.Add("element2")
  json = new Dictionary("empty" : new Collection, "filled" : c)
  msgbox json.ToString

but you will fail later, e.g. if you try to reparse it again:

dim json2 as new JSONItem(json.ToString) msgbox json2.ToString

[quote=69040:@Alwyn Bester]Ok, so I have been racking my brain on this issue, since there HAS to be an easier way to bypass the empty array issue instead of writing a whole new parser.

After re-reading the workaround given in the feedback ticket, it is obvious that one should simply subclass the JSONItem and then override the Contructor and ToString() function to intercept “[]” objects.

I’ve updated the test project with these changes. The “workaround” object now runs just as fast as the native JSONItem object, and empty arrays are correctly returned as “[]” by ToString. A custom JSONItem Exception class is also no longer needed.

The download link has been updated with the new class.[/quote]
Thanks for code Alwyn, but unfortunately I have the same problem as Tobias, the responses I’m getting from the service I’m using are generally keyed, so I’ll get a JSON string such as {“buckets”:[]} which your code will not import/export (toString) with the empty array intact.

Hmmm… the JSON spec clearly indicates that the empty array is a legitimate JSON construct… and the Xojo parser can’t handle it. So, what’s the new name for the jsonitem going to be in Xojo? :wink:

Really, this is one of those pain-in-the-backside bugs if you’re working very generally with JSON; I hope it gets fixed soon.

I only realized after Tobias made his post that the code only handles the top-level array item correctly, and doesn’t solve the problem for child nodes.

The code partially solves the problem in my current project, but I’m foreseeing that I will also eventually require a more permanent fix for this issue.

It looks like I’ve found another issue with JSONItem arrays.

I need to investigate further, but with a json string such as {“key”:[“el1”,“el2”]} I can’t test whether the value of “key” is an array.

[code]dim json as new JSONItem("{"“key”":["“el1"”,"“el2"”]}")

if json.HasName(“key”) then
// Get here OK.
if json.Value(“key”).IsArray then
// Can’t get here.
end if
end if
[/code]

That is probably because JSONItem.Value() returns a Variant. If you first cast the return value to a JSONItem it should work…

  dim json as new JSONItem("{""key"":[""el1"",""el2""]}")
  
  if json.HasName("key") then
    // Get here OK.
    if JSONItem(json.Value("key")).IsArray then
      // Should get here now.
    end if
  end if

Thanks Alwyn, forgot that Value() is a variant and that I should be testing Child() instead, thanks for the help.

Ended up with something a bit like…

[code] dim json as new JSONItem("{"“key”":["“el1"”,"“el2"”]}")

if json.HasName("key") and json.Child("key").IsArray then
  dim keyJSON As JSONItem = json.Child("key")
  
  dim key As String
  for i as integer = 0 to keyJSON.Count - 1
    key = keyJSON.Value(i)
    keys.Append(key)
  next
end if

[/code]

You’re welcome Ian. Using Child instead of Value with a cast is definitely much simpler.

I’ve noticed that <https://xojo.com/issue/25725> now shows “Fixed” in feedback.

Thanks for the fix Greg.

[quote=81462:@Alwyn Bester]I’ve noticed that <https://xojo.com/issue/25725> now shows “Fixed” in feedback.

Thanks for the fix Greg.[/quote]

Yes, thanks Greg, I made a little internal “woohoo!” when I got the email notification!

Looking forward to testing this in the next beta (assuming it makes the cut).

Turns out this bug is only half-fixed: if you use JSONItem.Value() = empty array, you get the same incorrect behavior.
See <https://xojo.com/issue/55093>