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).