"Pretty Print" JSON in new framework?

Does anybody have a solution? The current output really doesn’t play well with diff engines.

It has to work in the new framework without plugins, which makes the task especially difficult. If there is an engine out there I could potentially port, I’d be interested, but I haven’t found one yet.

I have a plugin…
You could optionally use it to parse a JSON and output it formatted.

like this:

dim j as new JSONMBS("{""Hello"":1}") MsgBox j.toString(true)

I wonder if you could do similar with some regex search & replaces.

Could you run it though the classic framework just for formatting?

It’s much too slow. Also means the behavior would be different between ios and non-ios.

Not sure if this is helpful, but I ported a C# JSON formatter a while back. It formats text so doesn’t depend on a framework:

https://blog.xojo.com/2015/08/14/format-json/

[quote=332429:@Paul Lefebvre]Not sure if this is helpful, but I ported a C# JSON formatter a while back. It formats text so doesn’t depend on a framework:

https://blog.xojo.com/2015/08/14/format-json/[/quote]
Exactly what I was hoping for. Assuming it’s fast enough, I’ll find out.

FYI, JSONFormatter.WriteCurrentLine uses EndOfLine which is a classic framework thing, so that should be changed. Luckily, that’s a piece of cake.

Damn. Looks like it’s too slow for my needs. Took over 2 minutes to convert the output.

Well, at the very least, I put in a feature request. <https://xojo.com/issue/48162>

Without having looked at the code Paul referenced, doing this in code is pretty easy. Shift the characters from the source to the destination according to these rules:

  • Add quotes and everything between them (included escaped quotes) as-is.
  • Ignore whitespace.
  • Add “,” as “,” + EOL + indent.
  • Add “:” as space + “:” + space.
  • On “{” or “[”, increment the indent, then add the character + EOL + indent.
  • On “}” or “]”, decrement the indent, then add EOL + indent + character.
  • Add anything else as-is.

I have the code for this lying around somewhere and, as I recall, it was pretty quick.

Here it is as code:

Public Function JSONPrettyPrint(json As Text) as Text
  const kBuffer as text = "  "
  const kEOL as text = &u0A
  
  dim outArr() as text
  dim indents( 0 ) as text
  
  dim addAsIs as boolean
  dim inQuote as boolean
  
  for each char as text in json.Characters
    if addAsIs then
      outArr.Append char
      addAsIs = false
      
    elseif char = """" then
      outArr.Append char
      inQuote = not inQuote
      
    elseif inQuote then
      outArr.Append char
      if char = "\" then
        addAsIs = true
      end if
      
    elseif char = "{" or char = "[" then
      indents.Append indents( indents.Ubound ) + kBuffer
      outArr.Append char
      outArr.Append kEOL
      outArr.Append indents( indents.Ubound )
      
    elseif char = "}" or char = "]" then
      call indents.Pop
      outArr.Append kEOL
      outArr.Append indents( indents.Ubound )
      outArr.Append char
      
    elseif char = "," then
      outArr.Append char
      outArr.Append kEOL
      outArr.Append indents( indents.Ubound )
      
    elseif char = ":" then
      outArr.Append " : "
      
    elseif char = &u0A or char = &u0D or char = " " or char = &u09 then
      //
      // Skip it
      //
      
    else
      outArr.Append char
      
    end if
  next
  
  dim result as text = Text.Join( outArr, "" )
  return result
End Function

5MB in about 2 seconds. I don’t think I’m going to see results better than that, except maybe once my feature request is implemented. Thank you very much Kem.

have you tried #pragma to disable background tasks.
and put json.Characters in a local variable first.

On a Mac, right? I expect it will be much slower than that on Windows/Linux. If this isn’t for iOS, convert to String and back.

[quote=332508:@Christian Schmitz]have you tried #pragma to disable background tasks.
and put json.Characters in a local variable first.
[/quote]

The way For Each works, I don’t think that matters.

Good point, I’ll need to test on Windows too. This is sometimes for iOS, so I try to avoid the classic framework whenever possible since Xojo hasn’t given us proper compilation constants to determine if the classic framework is available. We currently need to use #if Not TargetiOS, which is currently correct, but isn’t guaranteed to be in the future, such as when Android is released.

So for the record, when I tested on Windows, the result was impossibly slow. What would take about 2 seconds on Mac was taking longer than I could measure on Windows. Gave up waiting after what seemed like 10 minutes.

I finally got around to using a memoryblock. Times are now pretty consistent, with the time on Mac down to about 400ms and Windows to about 600ms.

[code]Function JSONPrettyPrint(json As Text) as Text
Const Indent = &h09
Const EndOfLine = &h0A

Dim Bytes() As UInt8
Dim Indents As UInteger

Dim AddAsIs, InQuote As Boolean

Dim Mem As Xojo.Core.MemoryBlock = Xojo.Core.TextEncoding.UTF8.ConvertTextToData(JSON)
Dim Bound As UInteger = Mem.Size - 1
For Offset As UInteger = 0 To Bound
Dim Char As UInt8 = Mem.UInt8Value(Offset)

If AddAsIs Then
  Bytes.Append(Char)
  AddAsIs = False
ElseIf Char = &h22 Then
  Bytes.Append(Char)
  InQuote = Not InQuote
ElseIf InQuote Then
  Bytes.Append(Char)
  If Char = &h5C Then
    AddAsIs = True
  End If
ElseIf Char = &h7B Or Char = &h5B Then
  Indents = Indents + 1
  Bytes.Append(Char)
  Bytes.Append(EndOfLine)
  For I As UInteger = 1 To Indents
    Bytes.Append(Indent)
  Next
ElseIf Char = &h7D Or Char = &h5D Then
  Indents = Indents - 1
  Bytes.Append(EndOfLine)
  For I As UInteger = 1 To Indents
    Bytes.Append(Indent)
  Next
  Bytes.Append(Char)
ElseIf Char = &h2C Then
  Bytes.Append(Char)
  Bytes.Append(EndOfLine)
  For I As UInteger = 1 To Indents
    Bytes.Append(Indent)
  Next
ElseIf Char = &h3A Then
  Bytes.Append(Char)
  Bytes.Append(&h20)
ElseIf Char = &h0A Or Char = &h0D Or Char = &h20 Or Char = &h09 Then
  // Skip it
Else
  Bytes.Append(Char)
End If

Next

Return Xojo.Core.TextEncoding.UTF8.ConvertDataToText(New Xojo.Core.MemoryBlock(Bytes))
End Function[/code]

Nice. You might gain a bit more if you access the bytes directly through the pointer rather than using the function. Store a reference to the pointer first, of course.

This is a nice project, but don’t understand why you need it.
I did a project with quite some json output last week and just used Notepadd++ with the Json plugin. All for free and perfect for the job. Think there must be a Mac tool somewhere too.
And, if you don’t want or have a tool locally, this one also does the job very well.

one reason :

MESSAGE File size is not supported more 1MB

I need it happen dynamically.

The reason is my document format is JSON. But a single-line JSON string doesn’t work nicely with version control. So I’ve had users request a “pretty” version to solve that problem, which I think is a perfectly valid request. Telling them to bring it to a website after saving is not a solution.

Pointer stuff is an area I never really got into, so what you’re describing is foreign to me. If I had to guess, you mean something like

Dim Mem As Xojo.Core.MemoryBlock = Xojo.Core.TextEncoding.UTF8.ConvertTextToData(JSON) Dim Bound As UInteger = Mem.Size - 1 Dim Pointer As Ptr = Mem For Offset As UInteger = 0 To Bound Dim Char As UInt8 = Pointer.UInt8(Offset) Next

But I can’t assign a Xojo.Core.MemoryBlock to a Ptr, though the docs tell me I should be able to.