Un modelo de objeto JSON rápido y eficiente en la memoria, con soporte para analizar y escribir eficientemente en formato compatible con JSON.
Esta biblioteca solo depende del repositorio de Neslib. Se incluye como submódulo con este repositorio.
El principal punto de entrada a esta biblioteca es la interfaz IJsonDocument . Se utiliza para analizar, cargar y guardar documentos JSON y proporciona acceso al modelo de objeto JSON. Puedes analizar una cadena JSON de la siguiente manera:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.Parse( ' { "Answer" : 42 } ' );
end ;Tenga en cuenta que, a diferencia de la especificación JSON oficial, esta biblioteca no requiere citas sobre las claves del diccionario (siempre que la clave no contenga espacios u otros caracteres no identificadores). Entonces, lo siguiente también es válido:
Doc := TJsonDocument.Parse( ' { Answer : 42 } ' ); También puede usar el método Load para cargar desde un archivo o transmisión.
En el lado de la salida, usa Save para guardar en un archivo o transmisión, o ToJson para emitir una cadena JSON.
También puede crear documentos New JSON desde cero utilizando los métodos CreateArray o CreateDictionary en:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.CreateArray;
Doc.Root.Add( 42 );
end ;Como puede ver en este ejemplo, accede al modelo de objeto de documento JSON a través de la propiedad raíz.
En el corazón del modelo JSON Object está el tipo TJsonValue . Este es un registro que puede contener cualquier tipo de valor JSON.
Proporciona varios operadores de conversión implícitos para convertir un TJsonValue a otro tipo (Delphi). Además, hay varios métodos To* que intentan convertir un TJsonValue pero devuelven un valor predeterminado proporcionado si la conversión falla.
Usted (nunca puede) crear el mismo TJsonValue ; La única forma de crear un TJsonValue es agregar un valor a la matriz o el diccionario JSON:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.CreateArray;
Doc.Root.Add( 42 );
end ; Este ejemplo agrega un TJsonValue (con el valor 42) a una matriz JSON. Para crear una nueva gama de diccionario, utiliza los métodos AddArray o AddDictionary :
var
Doc: IJsonDocument;
Dict: TJsonValue;
begin
Doc := TJsonDocument.CreateArray;
Dict := Doc.Root.AddDictionary;
Dict.AddOrSetValue( ' answer ' , 42 );
end ;Esto crea un nuevo diccionario y lo agrega a la matriz de raíces. Luego, el valor 42 se agrega a este diccionario bajo el nombre 'respuesta'.
Para verificar el tipo de valor, use la propiedad TJsonValue.ValueType o uno de los métodos TJsonValue.Is* .
Al intentar usar métodos como Add (o AddOrSetValue ) en valores que no son matrices (o diccionarios), se planteará una excepción.
Sin embargo, acceder a los elementos en una matriz (usando la propiedad Items ) o los valores en un diccionario (usando la propiedad Values ) nunca dará como resultado una excepción, incluso si el índice de matriz está fuera de los límites. Esto permite encadenar accesos de múltiples matrices/diccionario sin tener que verificar la validez de cada paso intermedio. Por ejemplo:
I := Doc.Root.Items[ 3 ].Values[ ' foo ' ].Values[ ' bar ' ].Items[ 4 ].ToInteger( 0 );Esto siempre tendrá éxito, pero devolverá 0 si alguno de los valores intermedios no está disponible.
La interfaz IJsonDocument hace que sea fácil de leer y escribir JSON en un modelo de objeto de documento.
Sin embargo, también puede elegir leer o escribir JSON manualmente si lo prefiere (por ejemplo, para evitar tener que cargar un modelo de objeto en la memoria). Puedes hacer esto con las interfaces IJsonReader e IJsonWriter en la unidad Neslib.Json.IO .
Estas interfaces son completamente independientes de cualquier implementación DOM y ni siquiera requieren la unidad Neslib.Json . Usar estas interfaces es un poco más complicado y requiere un poco más de trabajo. Consulte la unidad Neslib.Json.IO para obtener más información.
También hay una implementación de JSONPath similar a XPath que puede usar para consultar documentos JSON.
No hay una especificación oficial de JSONPath, pero la versión más utilizada parece ser una desarrollada por Stefan Goessner.
Un jsonpath parece:
$.store.book[0].titleo
$['store']['book'][0]['title'] Ambas representaciones son idénticas: puede usar la notación Dot ( . ) O soporte ( [] ) para denotar a los niños de un diccionario. Los soportes también se pueden usar con índices numéricos para denotar a los niños de una matriz por índice.
JSONPath solo usa citas individuales (') dentro de los soportes. También permitimos cotizaciones dobles (") ya que son más fáciles de usar en cadenas Delphi.
En breve:
$ que indica la raíz, seguida de cero o más operadores infantiles ( . O [] ). Un $ en sí mismo coincide con todo el documento.* o '*' ) para que coincida con todos los niños. Por ejemplo, $.store.book[*].author coincide con los autores de todos los libros en la tienda.. ), Se puede usar un punto doble ( .. ) para buscar descendientes en lugar de niños inmediatos. Por ejemplo, $..author coincide con todos los autores, independientemente de la profundidad. Esto se llama ascendencia recursiva.$.store.book[0,2,3] coincide con los libros primero, tercero y cuarto.[Start:End:Step] para que coincida con una porción (rango) de niños. Esto coincide con todos los niños desde Start del índice (pero no incluir) End , utilizando un tamaño Step dado (generalmente 1). Todos son opcionales, pero se debe dar al menos un valor (y colon):Start , se implica que es 0. Un valor negativo indica un desplazamiento desde el final de la matriz.End , el corte se extrae a través del final de la matriz. Un valor negativo indica y compensa desde el final de la matriz.Step , se implica que es 1.List[2:] coincide con el tercero y todos los elementos siguientes.List[-2:] coincide con los dos últimos elementos.List[:2] coincide con los dos primeros elementos.List[:-2] coincide con todos menos los dos últimos elementos.List[2:-2] coincide con todos los elementos, pero los dos primeros y los dos últimos.List[-4:-2] coincide con los elementos 3 y 4 del final.List[::2] coincide con todos los elementos con un índice uniforme.JSONPath también tiene un operador @ para permitir expresiones de script personalizadas. No admitimos este operador.
Documento de ejemplo:
{ "store" : {
"book" : [
{ "category" : " reference " ,
"author" : " Nigel Rees " ,
"title" : " Sayings of the Century " ,
"price" : 8.95
},
{ "category" : " fiction " ,
"author" : " J. R. R. Tolkien " ,
"title" : " The Lord of the Rings " ,
"isbn" : " 0-395-19395-8 " ,
"price" : 22.99
}
],
"bicycle" : {
"color" : " red " ,
"price" : 19.95
}
}
}Rutas de ejemplo:
| Expresión | Resultado |
|---|---|
$ | Coincide con el documento raíz (un solo valor) |
$..* | Coincide con todos los miembros en el documento (muchos valores) |
$.store.book[*].author | Los autores de todos los libros en la tienda |
$..author | Todos los autores |
$.store.* | Todas las cosas en la tienda (2 libros y una bicicleta) |
$.store..price | El precio de todo en la tienda |
$..book[2] | El tercer libro |
$..book[-1:] | El último libro en orden |
$..book[:2] | Los dos primeros libros |
La API JSONPATH es corta y simple. Consiste en un registro TJsonPath con solo un par de métodos.
Para una coincidencia única, use el método Match estática:
var
Doc: IJsonDocument;
Matches: TArray<TJsonValue>;
begin
Doc := TJsonDocument.Load(...);
Matches := TJsonPath.Match(Doc, ' $.store.book[*].author ' );
end ;Si planea usar la misma ruta en múltiples documentos (sub), entonces es más rápido analizar la ruta una vez, y luego aplicarla varias veces:
var
Doc1, Doc2: IJsonDocument;
Path: TJsonPath;
Matches1, Matches2: TArray<TJsonValue>;
begin
Doc1 := TJsonDocument.Load(...);
Doc2 := TJsonDocument.Load(...);
Path := TJsonPath.Create( ' $.store.book[*].author ' );
Matches1 := Path.Match(Doc1);
Matches2 := Path.Match(Doc2);
end ;También puedes ejecutar el camino en los sub-árboles:
var
Doc: IJsonDocument;
Store: TJsonValue;
Matches: TArray<TJsonValue>;
begin
Doc := TJsonDocument.Load(...);
Store := Doc.Root.Values[ ' store ' ];
Matches := TJsonPath.Match(Store, ' $.book[*].author ' );
end ; Si solo está interesado en una sola (o la primera) coincidencia, puede usar MatchSingle :
var
Doc: IJsonDocument;
Match: TJsonValue;
begin
Doc := TJsonDocument.Load(...);
if (TJsonPath.MatchSingle(Store, ' $.book[*] ' , Match)) then
...
end ; Toda la gestión de la memoria en esta biblioteca JSON es automática. Una interfaz IJsonDocument posee todo TJsonValue y los destruye cuando el documento se destruye (sale de alcance).
Lo único que debe tener en cuenta es que ya no debe usar ningún registro de TJSONValue después de que se destruya el documento. Hacerlo conducirá a un comportamiento indefinido y posiblemente se bloquee.
Puede personalizar algún comportamiento utilizando estas define condicionales:
JSON_UTF8 : para usar UTF8String en lugar de String en todas partes. Todas las cadenas se tratarán como cadenas UTF-8 de 8 bits en lugar de cadenas unicode de 16 bits. Esto reduce un poco el consumo de memoria y acelera un poco el análisis. Sin embargo, esto significa que también tendrá que usar esta biblioteca JSON con UTF8Strings, de lo contrario, Delphi se convertirá implícitamente entre Strings Unicode y UTF8Strings, lo que puede dañar el rendimiento.JSON_STRING_INTERNING : para habilitar String Interming para Dictionary Keys. Esto reduce el consumo de memoria en caso de que se use la misma clave muchas veces (lo cual es común cuando JSON se exporta desde una base de datos), pero es un poco más lento. La unidad Neslib.json declara el tipo JsonString como String o UTF8String , dependiendo de la definición de JSON_UTF8 . Sin embargo, esto no significa que también tenga que usar JsonString . Si no le importa la define JSON_UTF8 , entonces puede usar cadenas regulares con esta biblioteca.
Neslib.json tiene licencia bajo la licencia BSD simplificada.
Consulte License.txt para más detalles.