如何使用 DBXJSON 将带有转义/特殊字符的字符串与 JSON 相互转换?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11785963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 18:29:13  来源:igfitidea点击:

How do I convert a string to and from JSON with escaped/special characters using DBXJSON?

jsonstringdelphiescapingdbxjson

提问by Doug

I'm having trouble converting a string with escaped characters toand froma TJsonString. (I'm using Delphi XE 2, Update 4, Hotfix 1).

我无法用转义字符转换为字符串,以一个TJsonString。(我使用的是 Delphi XE 2、Update 4、Hotfix 1)。

NOTE: I am familiar with the SuperObject, but my requirements are to use the DBXJSON unit.

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

It looks like the TJSONString is not correctly escaped when returning the JSON representation via the ToString() method.

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

What (if anything) am I doing wrong and how do I correctly convert a string with special characters to/from its correct JSON representation?

我做错了什么(如果有的话),如何正确地将带有特殊字符的字符串转换为正确的 JSON 表示?

Perhaps I missed something, but none of the following Q&As seemed to address this directly:

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

EDIT:

As it turns out, the examples below were indeed working as expected.

What wasn't clear to me was that when creatinga TJSONString via it's constructor and adding it to a TJSONObject, the ToString() method will return an escapedrepresentation. However, after parsinga TJSONObject, the ToString() method will returned the un-escapedrepresentation.

The only other caveat was that the EscapeString() function in the sample code below was handling the double-quote. Although I wasn't using the double quote here, some of my other code was, and that caused the parsing to fail because TJSONString already escapes that character. I've updated my sample code to remove this handling from the EscapeString() function, which is what I've been using in my own classes.

Thanks again to @Linas for the answer, which helped me to "get" it.

编辑:

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

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

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

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

Raw String Value:

原始字符串值:

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

Text: c:\path\name
Next Line

What DBXJSON produces (NO ESCAPES):

DBXJSON 产生什么(没有转义):

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

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

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

Parsing UN-escaped Text FAILS:

解析未转义的文本失败

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

Parsed JsonObject = *NIL*

What I EXPECTDBXJSON to produce:

的处置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) (Text to parse validated with JSONLint):

解析 ESCAPED 文本(无效)(使用JSONLint进行解析验证的文本):

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

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

I've noticed that the only special character TJSONString seems to process correctly is the double quote (").

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

Here is the code I'm using:

这是我正在使用的代码:

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.

采纳答案by Linas

You can try to define your own TJSONString type and escape json strings there. E.g.:

您可以尝试定义自己的 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;

Usage example:

用法示例:

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;

And this is how parsing should be done:

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

//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;

回答by NightCabbage

We just ran into this lovely problem, where our backslashes weren't being escaped (but our double quotes were, apparently lol)...

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

The solution was to stop using TJSONObject.ToString(), and use TJSONObject.ToJSON()instead. This gives you the correctly escaped string, as opposed to the human readable format that ToString()returns.

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

Hope this helps someone :)

希望这对某人有所帮助:)