Generación de esquema JSON de un archivo XML con genson,lxml y xmltodict.

Posted on dom 28 junio 2020 in Tutorial de Python • 4 min read

La página de genson es la siguiente y en github. Lo primero que se hará es instalar genson , así como lxml y xmltodict .

Instalación

Se instala genson, lxml y xmltodict:

pip install genson lxml xmltodict

CLI Tool

Genson puede ser usado como un comando para generar un esquema json. A continuación se muestra la salida de su ayuda:

$ genson --help

usage: genson.py [-h] [-d DELIM] [-i SPACES] [-s SCHEMA] [-$ URI] ...

Generate one, unified JSON Schema from one or more JSON objects and/or JSON
Schemas. It's compatible with Draft 6 and above.

positional arguments:
  object                files containing JSON objects (defaults to stdin if no
                        arguments are passed)

optional arguments:
  -h, --help            show this help message and exit
  -d DELIM, --delimiter DELIM
                        set a delimiter - Use this option if the input files
                        contain multiple JSON objects/schemas. You can pass
                        any string. A few cases ('newline', 'tab', 'space')
                        will get converted to a whitespace character. If this
                        option is omitted, the parser will try to auto-detect
                        boundaries
  -i SPACES, --indent SPACES
                        pretty-print the output, indenting SPACES spaces
  -s SCHEMA, --schema SCHEMA
                        file containing a JSON Schema (can be specified
                        multiple times to merge schemas)
  -$ URI, --schema-uri URI
                        the value of the '$schema' keyword (defaults to
                        'http://json-schema.org/schema#' or can be specified
                        in a schema with the -s option). If 'NULL' is passed,
                        the "$schema" keyword will not be included in the
                        result.

GenSON Python API

Ahora se mostrará el uso del API de genSON para Python.

Ejemplos

Para todos los ejemplos se importará una lista de módulos de Python:

  • json
  • lxml parse
  • lxml XMLParse
  • xmltodict
  • genson SchemaBuilder
import json
from lxml.etree import parse
from lxml.etree import XMLParser
import xmltodict
import genson
from genson import SchemaBuilder

Una lista de diccionarios

Se tiene una lista de diccionarios de la siguiente forma:

templist = [{
 'name':'Sam',
},
{
 'name':'Jack',
}]

templist
[{'name': 'Sam'}, {'name': 'Jack'}]

Convertir a JSON la lista:

jsonf = json.dumps(templist)
jsonf 

'[{"name": "Sam"}, {"name": "Jack"}]'

Se crea la instancia del SchemaBuilder, se agrega el objeto y se muestra el esquema de ese objecto:

builder = SchemaBuilder()
builder.add_object(json.loads(jsonf))
builder.to_schema()

{'$schema': 'http://json-schema.org/schema#',
 'type': 'array',
 'items': {'type': 'object',
  'properties': {'name': {'type': 'string'}},
  'required': ['name']}}

El esquema muestra que el objeto es de tipo arreglo, que tiene items que son del tipo object, y que tiene un campo requerido como nombre, y ese nombre es de tipo string.

Diccionarios agregados como objetos individuales

Se agregarán dos diccionarios que tiene como clave "hi" con diferentes valores (tipo string y entero):

builder = SchemaBuilder()
builder.add_object({"hi": "there"})
builder.add_object({"hi": 5})
builder.to_schema()

{'$schema': 'http://json-schema.org/schema#',
 'type': 'object',
 'properties': {'hi': {'type': ['integer', 'string']}},
 'required': ['hi']}

Ahora el esquema devuelve que es de tipo objeto, y que tiene un campo requerido con nombre "hi", pero de tipo entero y string.

Se tiene una lista con dos items de distinto tipo (String, entero)

builder = SchemaBuilder()
builder.add_object(['one', 1])
builder.to_schema()

{'$schema': 'http://json-schema.org/schema#',
 'type': 'array',
 'items': {'type': ['integer', 'string']}}

El esquema que genera es de tipo arreglo, con item de dipo entero y string.

Caso real datos de la NASA

En el siguiente enlace encontrarán el data set de la NASA convertido en archivo plano en xml. El archivo xml lo pueden descargar directamente desde acá.

No se va a procesar o restructurar los datos, ya que sería necesario conocer el modelo de datos de la investigación.

Los pasos son los siguientes:

  1. Creación del parser xml con encoding UTF-8
parser = XMLParser(encoding='UTF-8')
  1. Leer los datos xml del archivo y convertirlos en un diccionario:
with open("../datos/nasa.xml", "r") as f:
  xx = xmltodict.parse(f.read())
  1. Crear la instancia de SchemaBuilder, pasarle el diccionario y generar el esquema:
sb = genson.SchemaBuilder()
sb.add_object(xx)
sb.to_schema()

{'$schema': 'http://json-schema.org/schema#',
 'type': 'object',
 'properties': {'datasets': {'type': 'object',
   'properties': {'dataset': {'type': 'array',
     'items': {'type': 'object',
      'properties': {'@subject': {'type': 'string'},
       '@xmlns:xlink': {'type': 'string'},
       'title': {'type': 'string'},
       'altname': {'type': 'array',
        'items': {'type': 'object',
         'properties': {'@type': {'type': 'string'},
          '#text': {'type': 'string'}},
         'required': ['#text', '@type']}},
       'reference': {'type': 'object',
................

Sólo se muestra una parte del esquema, lo que se hará a continuación es eliminar el "@" que en algunos datos aparecen.

Para ello se tiene una función de postprocesamiento:

def postprocessor(path,key,value):
    try: 
        if key.startswith("@"):
            key = "_" + key[1:]
        return key,value 
    except (ValueError, TypeError):
        return key,value

Toma las claves del diccionario y los que comiencen por "@" los cambia por "_".

  1. Se vuelve a leer los datos del archivo xml pasando la función:
with open("../datos/nasa.xml", "r") as f:
    xx = xmltodict.parse(f.read(),encoding="utf-8",postprocessor=postprocessor)
  1. Se crea la instancia de SchemaBuilder, se agrega el diccionario y se genera el esquema:
sb = genson.SchemaBuilder()
sb.add_object(xx)
sb.to_schema()

{'$schema': 'http://json-schema.org/schema#',
 'type': 'object',
 'properties': {'datasets': {'type': 'object',
   'properties': {'dataset': {'type': 'array',
     'items': {'type': 'object',
      'properties': {'_subject': {'type': 'string'},
       '_xmlns:xlink': {'type': 'string'},
       'title': {'type': 'string'},
       'altname': {'type': 'array',
        'items': {'type': 'object',
         'properties': {'_type': {'type': 'string'},
          '#text': {'type': 'string'}},
         'required': ['#text', '_type']}}

..................

Ahora se nota que cambiaron algunas claves con un "_".

Con xmltodict, lxl y genson se puede procesar conjunto de datos y restructurarlos a lo que se requiere para facilitar su análisis.

¡Haz tu donativo! Si te gustó el artículo puedes realizar un donativo con Bitcoin (BTC) usando la billetera digital de tu preferencia a la siguiente dirección: 17MtNybhdkA9GV3UNS6BTwPcuhjXoPrSzV

O Escaneando el código QR desde la billetera:

17MtNybhdkA9GV3UNS6BTwPcuhjXoPrSzV