简体   繁体   中英

Modify SuperObject to format/indent JSON string differently?

While using the SuperObject library, a single JSON object currently gets indented like so:

{
 "name": "value",
 "int_arr": [
  1,2,3],
 "obj_arr": [
  {
   "this": "that"
  },{
   "some": "thing"
  }],
 "another": 123
}

However, this indentation/formatting is not the same "user-friendly" or "human-readable" as JSON is all hyped up to be. I understand in computer language, this doesn't necessarily matter, but I'd like to format it like so instead:

{
  "name": "value",
  "int_arr": [1,2,3],
  "obj_arr": [
    {
      "this": "that"
    },
    {
      "some": "thing"
    }
  ],
  "another": 123
} 

For example, when using JSONLint to validate/format JSON code, it does so in a much cleaner manner.

How can I go about modifying the SuperObject library to format it differently? Is there a particular place in the library which defines these formatting rules? Or would I have to dig into the code in many different places to change this?

Thanks to the comment by David A , it was rather simple to implement these changes (after formatting the source and understanding how the library's code works). All the formatting is implemented in TSuperObject.Write , and all of such changes can be made here.

There was only one issue I could not figure out, which was arrays of types other than objects - the values will go to the next line. But at least array endings and arrays of objects have line breaks and indentation as desired.

Here's the modified version of TSuperObject.Write below (most subroutines not included to save space). Changes commented:

Constant:

const
  TOK_SP: PSOChar = #32#32; //<-- added another #32

Subroutine:

  procedure _indent(I: shortint; r: boolean);
  begin
    Inc(level, I);
    if r then
      with writer do
      begin
{$IFDEF MSWINDOWS}
        Append(TOK_CRLF, 2);
{$ELSE}
        Append(TOK_LF, 1);
{$ENDIF}
        for I := 0 to level - 1 do
          Append(TOK_SP, 2); //<-- changed 1 to 2
      end;
  end;

Procedure body:

begin

  if FProcessing then
  begin
    Result := writer.Append(TOK_NULL, 4);
    Exit;
  end;

  FProcessing := true;
  with writer do
    try
      case FDataType of
        stObject:
          if FO.c_object.FCount > 0 then
          begin
            k := 0;
            Append(TOK_CBL, 1);
            if indent then
              _indent(1, false);
            if ObjectFindFirst(Self, iter) then
              repeat
{$IFDEF SUPER_METHOD}
                if (iter.val = nil) or not ObjectIsType(iter.val, stMethod) then
                begin
{$ENDIF}
                  if (iter.val = nil) or (not iter.val.Processing) then
                  begin
                    if (k <> 0) then
                      Append(TOK_COM, 1);
                    if indent then
                      _indent(0, true);
                    Append(TOK_DQT, 1);
                    if escape then
                      DoEscape(PSOChar(iter.key), Length(iter.key))
                    else
                      DoMinimalEscape(PSOChar(iter.key), Length(iter.key));
                    if indent then
                      Append(ENDSTR_A, 3)
                    else
                      Append(ENDSTR_B, 2);
                    if (iter.val = nil) then
                      Append(TOK_NULL, 4)
                    else
                      iter.val.Write(writer, indent, escape, level);
                    Inc(k);
                  end;
{$IFDEF SUPER_METHOD}
                end;
{$ENDIF}
              until not ObjectFindNext(iter);
            ObjectFindClose(iter);
            if indent then
              _indent(-1, true);
            Result := Append(TOK_CBR, 1);
          end
          else
            Result := Append(TOK_OBJ, 2);
        stBoolean:
          begin
            if (FO.c_boolean) then
              Result := Append(TOK_TRUE, 4)
            else
              Result := Append(TOK_FALSE, 5);
          end;
        stInt:
          begin
            str(FO.c_int, st);
            Result := Append(PSOChar(SOString(st)));
          end;
        stDouble:
          Result := Append(PSOChar(SOString(gcvt(FO.c_double, 15, fbuffer))));
        stCurrency:
          begin
            Result := Append(PSOChar(CurrToStr(FO.c_currency)));
          end;
        stString:
          begin
            Append(TOK_DQT, 1);
            if escape then
              DoEscape(PSOChar(FOString), Length(FOString))
            else
              DoMinimalEscape(PSOChar(FOString), Length(FOString));
            Append(TOK_DQT, 1);
            Result := 0;
          end;
        stArray:
          if FO.c_array.FLength > 0 then
          begin
            Append(TOK_ARL, 1);
            if indent then
              _indent(1, true);
            k := 0;
            j := 0;
            while k < FO.c_array.FLength do
            begin

              val := FO.c_array.GetO(k);
{$IFDEF SUPER_METHOD}
              if not ObjectIsType(val, stMethod) then
              begin
{$ENDIF}
                if (val = nil) or (not val.Processing) then
                begin
                  if (j <> 0) then begin
                    Append(TOK_COM, 1);
                    if ObjectIsType(val, stObject) then begin //
                      if indent then                          //<-- create line break after object array items
                        _indent(0, true);                     //
                    end;                                      //
                  end;
                  if (val = nil) then
                    Append(TOK_NULL, 4)
                  else
                    val.Write(writer, indent, escape, level);
                  Inc(j);
                end;
{$IFDEF SUPER_METHOD}
              end;
{$ENDIF}
              Inc(k);
            end;
            if indent then
              _indent(-1, true); //<-- changed "false" to "true" to create line break at end of array
            Result := Append(TOK_ARR, 1);
          end
          else
            Result := Append(TOK_ARRAY, 2);
        stNull:
          Result := Append(TOK_NULL, 4);
      else
        Result := 0;
      end;
    finally
      FProcessing := false;
    end;
end;

That code would produce JSON data like so:

{
  "name": "value",
  "int_arr": [
    1,2,3
  ],
  "obj_arr": [
    {
      "this": "that"
    },
    {
      "some": "thing"
    }
  ],
  "another": 123
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM