Как преобразовать строку в JSON и из JSON с экранированными / специальными символами с помощью DBXJSON?
у меня возникли проблемы с преобразованием строки с экранированными символами to и С в TJsonString. (Я использую Delphi XE 2, Обновление 4, исправление 1).
примечание: Я знаком с SuperObject, но мои требования-использовать блок DBXJSON.
похоже, что tjsonstring неправильно экранируется при возврате представления JSON с помощью метода ToString ().
что (если что) я неправильно и как правильно преобразовать строку со специальными символами в / из ее правильного представления JSON?
возможно, я что-то пропустил, но ни один из следующих Q&As, казалось, не обращался к этому напрямую:
- Delphi декодирует JSON / utf8 экранированный текст
- библиотека Delphi JSON для XE2 доступна для сериализации объектов
- Delphi: JSON массив
- как разобрать вложенный объект JSON в Delphi XE2?
- десериализация вложенных объектов json с помощью Delphi 2012
EDIT:
как оказалось, приведенные ниже примеры действительно работали так, как ожидалось.
что мне было непонятно, так это когда создания TJSONString через его конструктор и добавление его в TJSONObject, Метод ToString () вернет бежал представление. Однако, после извлечение a TJSONObject, метод ToString () вернет un-Escape представление.
единственным другим предостережением было то, что функция EscapeString() в приведенном ниже примере кода обрабатывала двойную кавычку. Хотя я не использовал двойную кавычку здесь, некоторые из моих других кодов были, и это вызвало сбой синтаксического анализа, потому что TJSONString уже ускользает от этого персонажа. Я обновил свой пример кода, чтобы удалить эту обработку из функции EscapeString (), которую я использовал в своих собственных классах.
еще раз спасибо @Linas за ответ, который помог мне "получить" его.
Значение Строки Raw:
Text := 'c:pathname' +#13 + #10 + 'Next Line';
Text: c:pathname
Next Line
что производит DBXJSON (без побегов):
JsonString: "c:pathname
Next Line"
JsonPair: "MyString":"c:pathname
Next Line"
JsonObject: {"MyString":"c:pathname
Next Line"}
разбор не-экранированного текста не:
Text to parse: {"MyString":"c:pathname
Next Line"}
Parsed JsonObject = *NIL*
Что Я ожидал DBXJSON для производства:
Escaped String: c:pathnamernNext Line
JsonString: "c:pathnamernNext Line"
JsonPair: "MyString":"c:pathnamernNext Line"
JsonObject: {"MyString":"c:pathnamernNext Line"}
разбор экранированного текста (недействительным) (текст для разбора проверен с помощью JSONLint):
Text to parse: {"MyString":"c:pathnamernNext Line"}
Parsed JsonObject.ToString(): {"MyString":"c:pathname
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:pathname' +#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:pathname' +#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.
2 ответов
вы можете попытаться определить свой собственный тип 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;
мы просто столкнулись с этой прекрасной проблемой, где наши обратные косые черты не избегали (но наши двойные кавычки были, по-видимому, lol)...
решение состояло в том, чтобы прекратить использование ToString и вместо этого использовать ToJSON...
TJSONObject.Метод toJSON
Это дает вам правильно экранированную строку, в отличие от удобочитаемого формата, который возвращает ToString.
надеюсь, это кому-то поможет:)