【问题标题】:How do I convert a string to and from JSON with escaped/special characters using DBXJSON?如何使用 DBXJSON 将字符串与带有转义/特殊字符的 JSON 相互转换?
【发布时间】:2012-07-31 22:13:39
【问题描述】:

我在将带有转义字符 tofrom 的字符串转换为 TJsonString 时遇到问题。 (我使用的是 Delphi XE 2、Update 4、Hotfix 1)。

注意:我熟悉SuperObject,但我的要求是使用 DBXJSON 单元。

通过 ToString() 方法返回 JSON 表示时,TJSONString 似乎没有正确转义。

我做错了什么(如果有的话)以及如何正确地将带有特殊字符的字符串与其正确的 JSON 表示形式相互转换?

也许我错过了什么,但以下问答似乎都没有直接解决这个问题:

编辑:

事实证明,以下示例确实按预期工作。

我不清楚的是,当通过其构造函数创建 TJSONString 并将其添加到 TJSONObject 时,ToString() 方法将返回 转义 表示.但是,在解析 TJSONObject 之后,ToString() 方法将返回未转义 表示。

唯一需要注意的是,下面示例代码中的 EscapeString() 函数正在处理双引号。虽然我在这里没有使用双引号,但我的其他一些代码是,这导致解析失败,因为 TJSONString 已经转义了该字符。我更新了我的示例代码以从 EscapeString() 函数中删除此处理,这是我在自己的类中一直使用的。

再次感谢@Linas 的回答,帮助我“得到”了它。

原始字符串值:

Text := 'c:\path\name' +#13 + #10 + 'Next Line';

Text: c:\path\name
Next Line

DBXJSON 产生的内容(无转义):

JsonString: "c:\path\name
Next Line"

JsonPair: "MyString":"c:\path\name
Next Line"

JsonObject: {"MyString":"c:\path\name
Next Line"}

解析未转义的文本失败

Text to parse: {"MyString":"c:\path\name
Next Line"}

Parsed JsonObject = *NIL*

期望 DBXJSON 生成:

Escaped String: c:\\path\\name\r\nNext Line

JsonString: "c:\\path\\name\r\nNext Line"

JsonPair: "MyString":"c:\\path\\name\r\nNext Line"

JsonObject: {"MyString":"c:\\path\\name\r\nNext Line"}

Parsing ESCAPED Text (INVALID)(使用JSONLint 验证的要解析的文本):

Text to parse: {"MyString":"c:\\path\\name\r\nNext Line"}

Parsed JsonObject.ToString(): {"MyString":"c:\path\name
Next Line"}

我注意到唯一能正确处理的特殊字符 TJSONString 是双引号 (")。

这是我正在使用的代码:

program JsonTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, DbxJson;

function EscapeString(const AValue: string): string;
const
  ESCAPE = '\';
  // QUOTATION_MARK = '"';
  REVERSE_SOLIDUS = '\';
  SOLIDUS = '/';
  BACKSPACE = #8;
  FORM_FEED = #12;
  NEW_LINE = #10;
  CARRIAGE_RETURN = #13;
  HORIZONTAL_TAB = #9;
var
  AChar: Char;
begin
  Result := '';
  for AChar in AValue do
  begin
    case AChar of
      // !! Double quote (") is handled by TJSONString
      // QUOTATION_MARK: Result := Result + ESCAPE + QUOTATION_MARK;
      REVERSE_SOLIDUS: Result := Result + ESCAPE + REVERSE_SOLIDUS;
      SOLIDUS: Result := Result + ESCAPE + SOLIDUS;
      BACKSPACE: Result := Result + ESCAPE + 'b';
      FORM_FEED: Result := Result + ESCAPE + 'f';
      NEW_LINE: Result := Result + ESCAPE + 'n';
      CARRIAGE_RETURN: Result := Result + ESCAPE + 'r';
      HORIZONTAL_TAB: Result := Result + ESCAPE + 't';
      else
      begin
        if (Integer(AChar) < 32) or (Integer(AChar) > 126) then
          Result := Result + ESCAPE + 'u' + IntToHex(Integer(AChar), 4)
        else
          Result := Result + AChar;
      end;
    end;
  end;
end;

procedure Test;
var
  Text: string;
  JsonString: TJsonString;
  JsonPair: TJsonPair;
  JsonObject: TJsonObject;
begin
  try
    Writeln('Raw String Value');
    Writeln('-----------------');
    Text := 'c:\path\name' +#13 + #10 + 'Next Line';
    Writeln('Text: ', Text);
    JsonString := TJsonString.Create(Text);
    JsonPair := TJsonPair.Create('MyString', JsonString);
    JsonObject := TJsonObject.Create(JsonPair);
    // DBXJSON results
    Writeln;
    Writeln('What DBXJSON produces');
    Writeln('---------------------');
    Writeln('JsonString: ', JsonString.ToString);
    Writeln;
    Writeln('JsonPair: ', JsonPair.ToString);
    Writeln;
    Writeln('JsonObject: ', JsonObject.ToString);
    Writeln;

    // assign JSON representation
    Text := JsonObject.ToString;
    // free json object
    JsonObject.Free;
    // parse it
    JsonObject:= TJsonObject.ParseJsonValue(TEncoding.ASCII.GetBytes(
      Text), 0) as TJsonObject;
    Writeln('Parsing UN-escaped Text *FAILS* ');
    Writeln('----------------------------------');
    Writeln('Text to parse: ', Text);
    Writeln;
    if (JsonObject = nil) then
      Writeln('Parsed JsonObject = *NIL*')
    else
      Writeln('Parsed JsonObject: ', JsonObject.ToString);
    Writeln;
    // free json object
    JsonObject.Free;
    // expected results
    Text := 'c:\path\name' +#13 + #10 + 'Next Line';
    Text := EscapeString(Text);
    JsonString := TJsonString.Create(Text);
    JsonPair := TJsonPair.Create('MyString', JsonString);
    JsonObject := TJsonObject.Create(JsonPair);
    Writeln('What I *EXPECT* DBXJSON to produce');
    Writeln('----------------------------------');
    Writeln('Escaped String: ', Text);
    Writeln;
    Writeln('JsonString: ', JsonString.ToString);
    Writeln;
    Writeln('JsonPair: ', JsonPair.ToString);
    Writeln;
    Writeln('JsonObject: ', JsonObject.ToString);
    Writeln;
    // assign JSON representation
    Text := JsonObject.ToString;
    // free json object
    JsonObject.Free;
    // parse it
    JsonObject:= TJsonObject.ParseJsonValue(TEncoding.ASCII.GetBytes(
      Text), 0) as TJsonObject;
    Writeln('Parsing ESCAPED Text (*INVALID*) ');
    Writeln('----------------------------------');
    Writeln('Text to parse: ', Text);
    Writeln;
    Writeln('Parsed JsonObject.ToString(): ', JsonObject.ToString);
    Writeln;
    Readln;
  except
    on E: Exception do
    begin
      Writeln(E.ClassName, ': ', E.Message);
      Readln;
    end;
  end;
end;

begin
  Test;
end.

【问题讨论】:

    标签: json string delphi escaping dbxjson


    【解决方案1】:

    我在使用 Delphi XE2 时遇到了同样的问题。我的解决方案是更改库文件 Data.DBXJSON.pas。我将原始文件复制到我的 PatchedLibraryXE2 目录(我的库路径中的第一个)。这里我改了方法

    function TJSONString.ToString: UnicodeString;
    begin
      if FStrBuffer <> nil then
    //    Exit('"' + AnsiReplaceStr(FStrBuffer.ToString, '"', '\"') + '"');
        Exit('"' + EscapeValue(FStrBuffer.ToString) + '"');
      Result := NullString;
    end;
    

    我在上面的回答中使用了 Linas 的逃生方法。我忽略了大于 127 的字符的转义。为了编译,我还需要将文件 Data.DBXCommon.pas 复制到我的 PatchedLibraryXE2 目录中。现在我的TJSONObject.ToString() 可以正常工作了。

    【讨论】:

      【解决方案2】:

      我们刚刚遇到了这个可爱的问题,我们的反斜杠没有被转义(但我们的双引号显然是,哈哈)......

      解决方案是停止使用TJSONObject.ToString(),而改用TJSONObject.ToJSON()。这将为您提供正确转义的字符串,而不是 ToString() 返回的人类可读格式。

      希望这可以帮助某人:)

      【讨论】:

        【解决方案3】:

        您可以尝试定义自己的 TJSONString 类型并在那里转义 json 字符串。例如:

        uses
          DBXJSON;
        
        type
          TSvJsonString = class(TJSONString)
          private
            function EscapeValue(const AValue: string): string;
          public
            constructor Create(const AValue: string); overload;
          end;
        
        { TSvJsonString }
        
        constructor TSvJsonString.Create(const AValue: string);
        begin
          inherited Create(EscapeValue(AValue));
        end;
        
        function TSvJsonString.EscapeValue(const AValue: string): string;
        
          procedure AddChars(const AChars: string; var Dest: string; var AIndex: Integer); inline;
          begin
            System.Insert(AChars, Dest, AIndex);
            System.Delete(Dest, AIndex + 2, 1);
            Inc(AIndex, 2);
          end;
        
          procedure AddUnicodeChars(const AChars: string; var Dest: string; var AIndex: Integer); inline;
          begin
            System.Insert(AChars, Dest, AIndex);
            System.Delete(Dest, AIndex + 6, 1);
            Inc(AIndex, 6);
          end;
        
        var
          i, ix: Integer;
          AChar: Char;
        begin
          Result := AValue;
          ix := 1;
          for i := 1 to System.Length(AValue) do
          begin
            AChar :=  AValue[i];
            case AChar of
              '/', '\', '"':
              begin
                System.Insert('\', Result, ix);
                Inc(ix, 2);
              end;
              #8:  //backspace \b
              begin
                AddChars('\b', Result, ix);
              end;
              #9:
              begin
                AddChars('\t', Result, ix);
              end;
              #10:
              begin
                AddChars('\n', Result, ix);
              end;
              #12:
              begin
                AddChars('\f', Result, ix);
              end;
              #13:
              begin
                AddChars('\r', Result, ix);
              end;
              #0 .. #7, #11, #14 .. #31:
              begin
                AddUnicodeChars('\u' + IntToHex(Word(AChar), 4), Result, ix);
              end
              else
              begin
                if Word(AChar) > 127 then
                begin
                  AddUnicodeChars('\u' + IntToHex(Word(AChar), 4), Result, ix);
                end
                else
                begin
                  Inc(ix);
                end;
              end;
            end;
          end;
        end;
        

        使用示例:

        procedure Test;
        var
          LText, LEscapedText: string;
          LJsonString: TSvJsonString;
          LJsonPair: TJsonPair;
          LJsonObject: TJsonObject;
        begin
          LText := 'c:\path\name' + #13 + #10 + 'Next Line';
          LJsonString := TSvJsonString.Create(LText);
          LJsonPair := TJsonPair.Create('MyString', LJsonString);
          LJsonObject := TJsonObject.Create(LJsonPair);
          try
            LEscapedText := LJsonObject.ToString;
            //LEscapedText is: c:\\path\\name\r\nNext Line
          finally
            LJsonObject.Free;
          end;
        end;
        

        这就是应该如何进行解析:

        //AText := '{"MyString":"c:\\path\\name\r\nNext Line"}';
        function Parse(const AText: string): string;
        var
          obj: TJSONValue;
        begin
          obj := TJSONObject.ParseJSONValue(AText);
          try
            Result := obj.ToString;
            //Result := {"MyString":"c:\path\name
           //Next Line"}
          finally
            obj.Free;
          end;
        end;
        

        【讨论】:

        • 感谢@Linas,但我可以按照我的示例代码自行转义。另一个问题是,当 TJsonObject 包含转义字符串时,它似乎无法正确解析 JSON 字符串。我希望仍然是我做错了什么。 :)
        • @Doug 刚刚使用 Delphi XE 进行了测试,一切正常。您可能以错误的方式解析 json 字符串。请参阅我的答案编辑它应该如何完成。
        • 谢谢@Linas。在更详细地检查了我的代码之后,我发现我的转义例程正在处理双引号 (\")。问题是 TJSONString 已经处理了这种情况,并最终添加了一个额外的转义 (\\"),这导致解析失败。删除此案例后,一切正常。我已经更新了我的示例来解决这个问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-01
        • 2020-01-10
        • 2016-04-25
        • 2012-06-19
        • 2021-07-24
        相关资源
        最近更新 更多