Strategia di alto livello per distinguere una stringa regolare da JSON non valido (cioè rilevamento di stringhe JSON-like)

2

Disclaimer in assenza di codice:

Non ho codice da pubblicare perché non ho iniziato a scrivere; Stavo cercando una guida più teorica perché dubito che avrò dei problemi a codificarlo ma sono piuttosto confuso su quale approccio (i) potrebbe dare i migliori risultati. Non sto cercando alcun codice, neanche, però; solo direzione.

Dilemma

Sto cercando di aggiungere una funzione stile "metodo magico" a un'interfaccia utente che sto creando per un client, e richiederebbe in modo intelligente il rilevamento se una stringa fosse o meno destinata a essere JSON rispetto a una semplice stringa.

Ho preso in considerazione queste idee generali:

  1. Cerca una sorta di rapporto accettabile arbitrariamente determinato della frequenza della sintassi simile a JSON (ad esempio regex per trovare stringhe separate da due punti, cercare i due punti tra parentesi graffe, ecc.) al numero di stringhe incapsulate con quote + null s, bool s e int s / float s. Ma più piccolo è il set di dati, maggiore è la probabilità di ottenere questo

  2. cerca identificatori di chiavi come l'apertura e la chiusura di parentesi graffe ... non sono sicuro che ci siano più identificatori più facili, e questo non fa appello comunque perché è così prescrittivo sui tipi di errori che potrebbe trovare

  3. prova ad analizzare in modo incrementale i blocchi, come quelli tra parentesi graffe, e a vedere quale proporzione di queste affermazioni frazionarie risulta essere JSON valida; questo sembra che risentirebbe meno di (1) da dataset più piccoli, ma probabilmente sarebbe molto più intensivo in termini di elaborazione e molto suscettibile a una parentesi graffa mancante o invertita

È curioso che la gente computazionale o i professionisti dell'algoritmo là fuori avessero in mente qualche approccio che il mio cervello orientato alla semantica potrebbe aver perso.

PS: Mi sembra che l'elaborazione del linguaggio naturale, di cui sono totalmente all'oscuro, possa essere un approccio interessante; ma, se NLP è una buona strategia qui, non ha importanza perché non ho esperienza con esso e non ho tempo per imparare & quindi implementare / questa funzionalità non vale la pena per il cliente.

    
posta Jonline 30.05.2014 - 19:26
fonte

2 risposte

2

La risposta reale ("indica tra non valido JSON e una stringa")

Per molte ovvie ragioni non abbiamo una risposta definitiva - possiamo nel migliore dei casi sperare di determinare un indice che ci dirà se una stringa potrebbe essere stata JSON.

L'approccio migliore che riesco a pensare è quello di utilizzare qualcosa come l'algoritmo Levenshtein, con questa svolta: non abbiamo una "stringa B", ma piuttosto iniziamo ad analizzare la stringa candidata e la modifichiamo non appena avremo colpito un carattere non valido .

Per fare ciò, possiamo usare un parser di streaming JSON (basato sullo stato). Diciamo che analizziamo

{ "key": value" }

allora il parser troverà la "v" e soffocherà. A quel punto possiamo provare a cancellare il personaggio, o aggiungere uno dei tanti possibili caratteri validi per quello stato e recurse.

Qualsiasi ramo può essere abbandonato non appena la distanza dal JSON valido per quel ramo supera un minimo, se ne è stato trovato uno fino a quel momento, o secondo una certa euristica (per esempio non più di quattro consecutive [e così via ), o non appena supera una distanza massima consentita (per esempio 5). Quest'ultima condizione è probabilmente essenziale per evitare lo snowballing.

Un inconveniente di questo approccio è che alcune soluzioni richiedono il backtracking:

{ "key": "value }

verrebbe accettato fino alla fine, quindi verrà aggiunto "mancante" e infine mancante}, ottenendo un JSON valido:

{ "key": "value }"}

con una distanza di 2 (due aggiunte). Forse può essere trovata una certa euristica veloce per il backtracking (ad es. In "wait-for-end-quote", tornare indietro fino al primo carattere non "speciale" e ignorare gli spazi bianchi).

Un elemento importante per valutare la fattibilità di una soluzione sarebbe come viene generato questo JSON non valido , cioè è un JSON troncato? Alcuni personaggi sono danneggiati? ha una sorta di struttura di base che è sempre la stessa?

JSON generato dall'utente

Come da commento, questo JSON potrebbe essere il risultato di un tentativo di costruire manualmente JSON. Questo da un lato è cattivo dal momento che gli esseri umani sono molto vicini a una fonte casuale quando parliamo di errori :-). D'altra parte può essere buono dato che il JSON è probabilmente "quasi buono JSON" con forse solo alcuni errori: virgole lasciate fuori o aggiunte,

{ "first_name": "John", "last_name": "Doe", "email": "[email protected]", }

In effetti, potremmo trovare utile almeno preelaborare la stringa per rimuovere alcuni errori comuni:

", \ s *} - >"} # Virgola extra a causa di copia-incolla    (") \ s * [:;.] \ s (" [^ "] " \ s :) # Ha dimenticato o digitato in modo errato una virgola tra 1 e 2

e dal momento che (si spera) le parentesi graffe e quadrate non appariranno all'interno di chiavi o valori, possiamo provare a bilanciarli e / o usarli come guida: ad es. se le parentesi graffe sono bilanciate, supponi che siano a posto e non aggiungere o rimuovere nessuna di esse. Questo deve essere ponderato rispetto alla capacità di errore degli utenti - se sono in grado di sbagliare completamente una parentesi graffa di chiusura, allora non possiamo fare affidamento sul bilanciamento.

(Naturalmente, non possiamo fare nulla contro un utente malizioso che alimenta intenzionalmente JSON malformato progettato per ingannare il sistema, o anche causare un rifiuto del servizio attraverso alcune espressioni che richiedono un calcolo esponenziale del tempo) .

A quel punto potremmo invece, come hai suggerito, considerare la frequenza dei caratteri di controllo JSON, o provare a riconoscere le sottosezioni JSON. Questo deve essere confrontato con qualsiasi cosa altrimenti potrebbe entrare in quel campo.

La cattiva risposta ("indica tra valido JSON e una stringa")

Nel caso generale non puoi distinguere con accuratezza del 100% tra stringhe e JSON perché, mentre non tutti i JSON sono stringhe valide, tutte le stringhe valide sono JSON validi . La codifica di una stringa nuda è quella stringa.

Quindi alla ricezione di "Hello, world" , qualsiasi funzione restituirebbe che questo è entrambi una stringa valida e un JSON valido. Quale dovrebbe scegliere?

Altrimenti, distinguere è piuttosto semplice. Collega il motore JSON di tua scelta e prova a decodificare la stringa come JSON. Se puoi, è una stringa JSON. Se non puoi, è una stringa di testo:

-- pseudo code
with ( JSON.decode(myString) ) {
    if (.isNull()) {
        -- Error. Not valid JSON, so it must be a String.
        return "String"
    }
    -- It is a valid JSON, but a String is that, too.
    if (.type().equals("String")) {
        -- Could return either, too, depending.
        return "Ambiguous"
    }
}
return "JSON"

Questo non è sicuramente efficiente come una funzione che solo controlla i vincoli grammaticali (puoi trovare gli schemi su home page ), ma è molto più gestibile. Probabilmente puoi ottenere un FSM facendo bollire a secco il codice di un parser di streaming e rimuovendo tutte le emissioni di dati, restituendo semplicemente "true" o "false" (un errore di elaborazione che significa che non è JSON valido).

    
risposta data 30.05.2014 - 22:30
fonte
1

Quindi quello che vuoi è uno sniffer JSON. Qualcosa da guardare una stringa e fare un'ipotesi euristica. Che cosa cercare e come cercare?

Prima di tutto, un parser JSON è abbastanza inutile. Chiami che dopo hai indovinato che probabilmente è JSON.

Posso pensare a tre approcci.

  1. Pistola fumante. Se la stringa contiene JSON o json-schema.org, è una corrispondenza. Forse ci sono altre parole comuni che potresti conoscere.
  2. Registra. Pensa ad alcune regex che avrebbero molte probabilità di essere abbinate. Se oltre la metà delle righe corrisponde a una o più espressioni regolari appropriate, è una corrispondenza.
  3. Tokenizzalo. In genere, JSON ha lotti di singole stringhe, numeri, virgole, parentesi, parentesi e due punti citati. Qualsiasi stringa superiore al 50% composta da quei token è una corrispondenza. (Assicurati di non lasciare che una citazione senza corrispondenza ingoia l'intera stringa.)

I tuoi suggerimenti potrebbero funzionare anche, ma penso che i miei siano più semplici.

Ho usato il codice sniffing in passato ed è sorprendentemente facile farlo bene la maggior parte del tempo. Di solito gli errori si verificano quando ottieni (diciamo) un messaggio di testo con un pezzo di JSON quotato in esso. Non puoi vincerli tutti.

    
risposta data 08.06.2014 - 16:18
fonte

Leggi altre domande sui tag