2012-08-02 25 views
12

Tengo problemas para convertir una cadena con caracteres escapados a y de a TJsonString. (Estoy usando Delphi XE 2, Update 4, Hotfix 1).¿Cómo convierto una cadena hacia y desde JSON con caracteres escapados/especiales usando DBXJSON?

NOTA: Estoy familiarizado con el SuperObject, pero mis requisitos son utilizar la unidad DBXJSON.

Parece que TJSONString no se escapó correctamente al devolver la representación JSON mediante el método ToString().

¿Qué (si acaso) estoy haciendo mal y cómo convierto correctamente una cadena con caracteres especiales en/desde su representación correcta de JSON?

Tal vez me haya perdido algo, pero ninguno de los siguientes Q & como parecía hacer frente a esto directamente:

EDIT:

Como resultado, los ejemplos a continuación fueron de hecho funcionando como se esperaba.

Lo que no estaba claro para mí fue que cuando crear un TJSONString través de ella de constructor y agregarlo a un TJSONObject, el método ToString() devolverá un escaparon representación. Sin embargo, después de analizar un TJSONObject, el método ToString() devolverá la representación de no escapada.

La única otra advertencia era que la función EscapeString() en el código de ejemplo siguiente manejaba la comilla doble. Aunque no estaba usando la comilla doble aquí, parte de mi otro código era, y eso hizo que el análisis fallara porque TJSONString ya se escapa de ese carácter. He actualizado mi código de muestra para eliminar este manejo de la función EscapeString(), que es lo que he estado usando en mis propias clases.

Gracias de nuevo a @Linas por la respuesta, que me ayudó a "obtener".

Raw Valor de cadena:

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

Text: c:\path\name 
Next Line 

Lo que produce DBXJSON (no se escape):

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

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

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

análisis de texto escapado por la ONU FALLA:

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

Parsed JsonObject = *NIL* 

Lo que ESPERO DBXJSON para producir:

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"} 

de análisis ESCAPED Texto (VÁLIDA) (Texto para analizar validado con JSONLint):

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

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

Me di cuenta de que el único carácter especial que TJSONString parece procesar correctamente es la comilla doble (").

Aquí está el código que estoy usando:

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. 

Respuesta

7

Usted puede tratar de definir su propio tipo TJSONString y escapar de las cadenas JSON allí. Por ejemplo: ejemplo

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; 

Uso:

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; 

Y así es como se debe hacer el análisis sintáctico:

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

Gracias @Linas, pero soy capaz de hacer lo mismo escapando según mi código de muestra. El otro problema es que TJsonObject no parece poder analizar la cadena JSON correctamente cuando contiene cadenas escapadas. Espero que sigo siendo yo el que está haciendo algo mal. :) – Doug

+0

@Doug Acabo de probar con Delphi XE y todo funciona como se esperaba. Probablemente esté analizando cadena json de una manera incorrecta. Vea mi respuesta editar cómo se debe hacer. – Linas

+0

Gracias @Linas. Después de examinar mi código con más detalle, encontré que mi rutina de escape manejaba la comilla doble (\ "). El problema es que TJSONString ya maneja este caso, y terminó agregando un escape adicional (\\"), que causó el análisis para fallar Después de eliminar este caso, todo funcionó bien. He actualizado mi ejemplo para arreglar esto. – Doug

Cuestiones relacionadas