Delphi декодирует экранированный текст json/utf8

Я пишу модуль для сложного приложения, и мой модуль должен обрабатывать ответ json, возвращаемый веб-сервером. Итак, моя проблема в том, как я могу декодировать такой текст:

\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0444\u0430\u043c\u0438\u043b\u0438\u0438, \u0438\u043c\u0435\u043d\u0438 (\u043e\u0442\u0447\u0435\u0441\u0442\u0432\u0443

Это кириллический текст, и Mozilla Firefox отображает его так, как должно быть. Как я могу обработать это, ребята? У меня Делфи 2010.


person kseen    schedule 15.03.2012    source источник
comment
Ваша библиотека декодирования JSON уже должна справиться с этим за вас. Какую библиотеку вы используете? Кроме того, это недопустимый JSON; вы уверены, что на самом деле это не внутри строки?   -  person Rob Kennedy    schedule 15.03.2012
comment
Я не понимаю этого, используя какую-то библиотеку JSON. Я просто получаю это, используя необработанные запросы HTTP GET.   -  person kseen    schedule 15.03.2012
comment
Я понимаю это, но если то, что вы получаете, должно быть закодировано в JSON, как вы говорите, то вам следует использовать библиотеку декодирования JSON для его обработки вместо того, чтобы писать свою собственную.   -  person Rob Kennedy    schedule 15.03.2012
comment
Да, это так. RRUZ ниже предложил для этого хорошую библиотеку.   -  person kseen    schedule 15.03.2012
comment
Это не UTF-8, а строка, содержащая экранированные кодовые точки UTF-16.   -  person OnTheFly    schedule 15.03.2012
comment
Я вижу обратную косую черту, пробелы, цифры, латинские буквы, запятую и скобку @User. Все это кодовые точки UTF-8.   -  person Rob Kennedy    schedule 15.03.2012
comment
@RobK, ты, наверное, ищешь что-то другое. Попробуйте прочитать спецификацию — tools.ietf.org/html/rfc4627.   -  person OnTheFly    schedule 15.03.2012
comment
Я смотрю на второй абзац этого вопроса, @User. Тот, который начинается с \u041f\u043e. Он явно квалифицируется как текст UTF-8. Это строка кодовых точек UTF-8, представляющая escape-последовательности для кодовых точек UTF-16. В конце концов, кодировка по умолчанию для текста JSON — UTF-8.   -  person Rob Kennedy    schedule 16.03.2012
comment
@RobKennedy, хех, да, кодировка самого JSON должна быть Unicode, и ее можно определить, изучив нулевой шаблон. Но проверьте карту символов Windows на наличие кодовых точек в кавычках и сравните найденные глифы со скриншотом в ответе RRUZ :-)   -  person OnTheFly    schedule 16.03.2012


Ответы (3)


Вы можете использовать модуль DBXJSON, включенный в Delphi 2010.

uses
 DBXJSON;

const
JsonUt8  ='"\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0444\u0430\u043c\u0438\u043b\u0438\u0438, \u0438\u043c\u0435\u043d\u0438 (\u043e\u0442\u0447\u0435\u0441\u0442\u0432\u0443"';

procedure TForm59.Button1Click(Sender: TObject);
var
  LJSONValue: TJSONValue;
begin
  LJSONValue:=TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(JsonUt8),0);
  Edit1.Text:=LJSONValue.ToString;
end;

введите здесь описание изображения

person RRUZ    schedule 15.03.2012
comment
Хорошо выглядишь, спасибо. Возможно, мне стоит пойти с этой библиотекой. - person kseen; 15.03.2012

Хорошо, ребята, вот полный код, который помогает мне справиться с этой проблемой:

function Unescape(const s: AnsiString): string;
var
  i: Integer;
  j: Integer;
  c: Integer;
begin
  // Make result at least large enough. This prevents too many reallocs
  SetLength(Result, Length(s));
  i := 1;
  j := 1;
  while i <= Length(s) do begin
    if s[i] = '\' then begin
      if i < Length(s) then begin
        // escaped backslash?
        if s[i + 1] = '\' then begin
          Result[j] := '\';
          inc(i, 2);
        end
        // convert hex number to WideChar
        else if (s[i + 1] = 'u') and (i + 1 + 4 <= Length(s))
                and TryStrToInt('$' + string(Copy(s, i + 2, 4)), c) then begin
          inc(i, 6);
          Result[j] := WideChar(c);
        end else begin
          raise Exception.CreateFmt('Invalid code at position %d', [i]);
        end;
      end else begin
        raise Exception.Create('Unexpected end of string');
      end;
    end else begin
      Result[j] := WideChar(s[i]);
      inc(i);
    end;
    inc(j);
  end;

  // Trim result in case we reserved too much space
  SetLength(Result, j - 1);
end;

const
  NormalizationC = 1;

function NormalizeString(NormForm: Integer; lpSrcString: PWideChar; cwSrcLength: Integer;
 lpDstString: PWideChar; cwDstLength: Integer): Integer; stdcall; external 'Normaliz.dll';

function Normalize(const s: string): string;
var
  newLength: integer;
begin
  // in NormalizationC mode the result string won't grow longer than the input string
  SetLength(Result, Length(s));
  newLength := NormalizeString(NormalizationC, PChar(s), Length(s), PChar(Result), Length(Result));
  SetLength(Result, newLength);
end;

function UnescapeAndNormalize(const s: AnsiString): string;
begin
  Result := Normalize(Unescape(s));
end;

Код украден из этот ответ

person kseen    schedule 15.03.2012
comment
Есть ли способ реализовать это в D2007? Где отсутствие юникода помешает этому? - person M Schenkel; 01.01.2013

В случае, если кому-то понадобится простая функция для декодирования экранированной строки JSON:

function JSONUnescape(const Source: string; CRLF: string = #13#10): string;
const
  ESCAPE_CHAR = '\';
  QUOTE_CHAR = '"';
  EXCEPTION_FMT = 'Invalid escape at position %d';
var
  EscapeCharPos, TempPos: Integer;
  Temp: string;
  IsQuotedString: Boolean;
begin
  result := '';
  IsQuotedString := (Source[1] = QUOTE_CHAR) and
    (Source[Length(Source)] = QUOTE_CHAR);
  EscapeCharPos := Pos(ESCAPE_CHAR, Source);
  TempPos := 1;
  while EscapeCharPos > 0 do
  begin
    result := result + Copy(Source, TempPos, EscapeCharPos - TempPos);
    TempPos := EscapeCharPos;
    if EscapeCharPos < Length(Source) - Integer(IsQuotedString) then
      case Source[EscapeCharPos + 1] of
        't':
          Temp := #9;
        'n':
          Temp := CRLF;
        '\':
          Temp := '\';
        '"':
          Temp := '"';
        'u':
          begin
            if EscapeCharPos + 4 < Length(Source) - Integer(IsQuotedString) then
              Temp := Chr(StrToInt('$' + Copy(Source, EscapeCharPos + 2, 4)))
            else
              raise Exception.Create(Format(EXCEPTION_FMT, [EscapeCharPos]));
            Inc(TempPos, 4);
          end;
      else
        raise Exception.Create(Format(EXCEPTION_FMT, [EscapeCharPos]));
      end
    else
      raise Exception.Create(Format(EXCEPTION_FMT, [EscapeCharPos]));
    Inc(TempPos, 2);
    result := result + Temp;
    EscapeCharPos := Pos(ESCAPE_CHAR, Source, TempPos);
  end;
  result := result + Copy(Source, TempPos, Length(Source) - TempPos + 1);
end;

Использование:

JSONUnescape('\u2764Love Delphi\u2764');
// Returns '❤Love Delphi❤'
JSONUnescape('"\u2764Love\tDelphi\u2764"');
// Returns '"❤Love  Delphi❤"';
JSONUnescape('\\\Invalid escaped text');
// Raises and exception 'Invalid escape at position 3'
person Olvin Roght    schedule 20.11.2020