JSON.stringify sans guillemets sur les propriétés?

95

J'utilise un service qui utilise un format JSON incorrect (pas de guillemets doubles autour des propriétés). Alors j'ai besoin d'envoyer

{ name: "John Smith" } au lieu de { "name": "John Smith" }

Ce format ne peut pas être modifié car ce n'est pas mon service.

Quelqu'un connaît-il un routage stringify pour formater un objet JavaScript comme ci-dessus?

jadent
la source

Réponses:

115

Cette solution d'expression régulière simple fonctionne pour décompresser les noms de propriété JSON dans la plupart des cas:

const object = { name: 'John Smith' };
const json = JSON.stringify(object);  // {"name":"John Smith"}
console.log(json);
const unquoted = json.replace(/"([^"]+)":/g, '$1:');
console.log(unquoted);  // {name:"John Smith"}

Cas extrême:

var json = '{ "name": "J\\":ohn Smith" }'
json.replace(/\\"/g,"\uFFFF");  // U+ FFFF
json = json.replace(/"([^"]+)":/g, '$1:').replace(/\uFFFF/g, '\\\"');
// '{ name: "J\":ohn Smith" }'

Un merci spécial à Rob W pour l'avoir réparé.

Limites

Dans des cas normaux, l'expression rationnelle susmentionnée fonctionnera, mais mathématiquement, il est impossible de décrire le format JSON avec une expression régulière telle qu'elle fonctionnera dans tous les cas (compter le même nombre d'accolades est impossible avec l'expression rationnelle). Par conséquent, j'ai créer une nouvelle fonction pour supprimer les guillemets en analysant formellement la chaîne JSON via la fonction native et la resérialiser:

function stringify(obj_from_json) {
    if (typeof obj_from_json !== "object" || Array.isArray(obj_from_json)){
        // not an object, stringify using native function
        return JSON.stringify(obj_from_json);
    }
    // Implements recursive object serialization according to JSON spec
    // but without quotes around the keys.
    let props = Object
        .keys(obj_from_json)
        .map(key => `${key}:${stringify(obj_from_json[key])}`)
        .join(",");
    return `{${props}}`;
}

Exemple: https://jsfiddle.net/DerekL/mssybp3k/

Derek 朕 會 功夫
la source
3
Je n'étais pas moi non plus, mais vous devez lire la question attentivement. OP doit encoder un objet en json (cassé), pas l'analyser / l'évaluer.
Salman A
7
@Derek Cette méthode n'est pas fiable . Par exemple, prenez cette entrée: {"foo":"e\":bar"}(JSON valide) devient {foo:e:bar"}(...)!
Rob W
1
@Derek /\\\"/peut être simplifié en /\\"/. N'oubliez pas d'ajouter l'indicateur global /\\"/g, sinon il se cassera sur les chaînes avec plusieurs \". Comme pour le caractère aléatoire, n'utilisez jamais un littéral U + FFFF, au cas où l'éditeur s'étouffe, mais une séquence d'échappement. Le regex pour revenir deviendrait /\uFFFF/g.
Rob W
2
@Derek 朕 會 功夫 votre expression régulière /\"([^(\")"]+)\":/gpeut être simplifiée /"([^"]+)":/g, voir regex101.com/r/qnz0ld/2
tanguy_k
1
@endriu Dans ce cas, ajoutez simplement une vérification supplémentaire pour les valeurs nulles.
Derek 朕 會 功夫
18

Il semble que ce soit une méthode Object toString simple que vous recherchez.

Dans Node.js, cela est résolu en utilisant l'objet util et en appelant util.inspect (yourObject). Cela vous donnera tout ce que vous voulez. suivez ce lien pour plus d'options, y compris la profondeur de l'application de la méthode. http://nodejs.org/api/util.html#util_util_inspect_object_options

Donc, ce que vous recherchez est essentiellement un inspecteur d'objets et non un convertisseur JSON. Le format JSON spécifie que toutes les propriétés doivent être placées entre guillemets. Par conséquent il n'y aura pas de convertisseurs JSON pour faire ce que vous voulez que cela est tout simplement pas un JSON format.Specs ici: https://developer.mozilla.org/en-US/docs/Using_native_JSON

Objet à chaîne ou inspection est ce dont vous avez besoin en fonction de la langue de votre serveur.

fino
la source
1
Merci beaucoup! Ceci est exactement ce que je cherchais. J'utilise json pour émettre des données sur le serveur ws vers mon jeu et croyez-le ou non, ne pas avoir à gérer les guillemets supplémentaires autour des noms de propriété permet d'économiser une immense quantité de données! Juste pour clarifier, .toSource()fonctionne bien dans nodejs aussi, mais ne fonctionne pas avec les objets dans les tableaux. L' utilinspection fonctionne pour les tableaux et les objets dans les tableaux, ce qui est merveilleux, j'adore.
NiCk Newman
3
util.inspect()a fonctionné à merveille pour moi en écrivant un objet dans une requête Neo4j, pour définir plusieurs paramètres à la fois.
agm1984
1
Depuis le lien nodejs:> La méthode util.inspect () retourne une représentation sous forme de chaîne d'objet destinée au débogage. La sortie de util.inspect peut changer à tout moment et ne doit pas dépendre du programme.
Peter Roehlen
5

Vous pouvez consulter le code source d' un analyseur créé par celui qui a défini le format JSON . Recherchez les appels de fonction: ceux-ci entourent une valeur entre guillemets. Les clés sont citées aux lignes 326 et 338 .json2.js quote

N'incluez pas la bibliothèque après la modification. Au lieu de cela, prenez uniquement la partie pertinente ( stringify), ou au moins remplacez-la JSONpar autre chose, par exemple. FAKEJSON.

Par exemple, un objet FAKEJSONqui ne définit que stringify: http://jsfiddle.net/PYudw/

Rob W
la source
Pourquoi auriez-vous besoin d'une bibliothèque supplémentaire alors que vous pouvez le faire en JavaScript pur?
Derek 朕 會 功夫
C'est une bonne idée. Je bifurquerais le dépôt, supprimerais les guillemets et renommerais l'objet JSON en quelque chose de facétieux comme FAILJSON pour indiquer clairement que vous ne travaillez pas avec l'objet JSON réel ou le JSON réel.
RichardTowers
@Derek La bibliothèque ne doit pas être incluse dans son intégralité. Ne prenez que la JSON.stringifypartie et supprimez les guillemets. Puisque la bibliothèque est créée par celui qui a défini JSON , nous pouvons être à peu près sûrs que le résultat est un JSON très valide.
Rob W
Il semble que ce soit la meilleure approche.
Derek 朕 會 功夫
Ouais, d'accord avec Derek. Bien que son remplaçant fonctionne bien, je me sens plus en confiance avec le code de crawford, sans manquer de respect à derek lol. .toSource()fonctionne bien mais n'inclut pas si votre objet est dans un tableau, ce qui est décevant (et je suis sur le nœud, donc la compatibilité du navigateur n'est pas un problème: P) donc j'utiliserai cette méthode grâce à @RobW également, le lien jsfiddle semble être coincé à la page de chargement :(
NiCk Newman
3

Essayez d'utiliser le servive avec JSONP, je suppose qu'ils l'offrent lors de l'utilisation de ce format.

Sinon, déposez-leur un rapport de bogue détaillé comprenant une bonne argumentation pour expliquer pourquoi le devrait être conforme à la norme. Toute autre solution que l'élimination du problème source n'est pas une vraie solution.

Une solution rapide et sale pourrait être de diriger la chaîne à travers une expression régulière avant de l'analyser:

var obj = JSON.parse(str.replace(/(\{|,)\s*(.+?)\s*:/g, '$1 "$2":'));

Ou vous essayez d'ajuster un analyseur javascript JSON existant (comme celui-ci ) si vous voulez une analyse plus syntaxique.

Bergi
la source
3

J'ai trouvé un bon paquet NPM pour faire exactement cela:

https://www.npmjs.com/package/stringify-object

const stringify = require('stringify-object')

let prettyOutput = stringify(json);

Fonctionne plutôt bien.

user1543276
la source
1
Cette bibliothèque n'est pas récursive.
corysimmons
2

Votre syntaxe héritée devrait être facilement mangée par YAML, qui est un sur-ensemble de JSON.

Essayez l'analyseur et le dumper JavaScript YAML: http://nodeca.github.com/js-yaml/

user1649339
la source
2

@Derek 朕 會 功夫 Merci d'avoir partagé cette méthode, j'aime partager mon code qui prend également en charge la stringification d'un tableau d'objets.

export const stringifyObjectWithNoQuotesOnKeys = (obj_from_json) => {
    // In case of an array we'll stringify all objects.
    if (Array.isArray(obj_from_json)) {
        return `[${
                    obj_from_json
                        .map(obj => `${stringifyObjectWithNoQuotesOnKeys(obj)}`)
                        .join(",")
                }]` ;
    }
    // not an object, stringify using native function
    if(typeof obj_from_json !== "object" || obj_from_json instanceof Date || obj_from_json === null){
        return JSON.stringify(obj_from_json);
    }
    // Implements recursive object serialization according to JSON spec
    // but without quotes around the keys.
    return `{${
            Object
                .keys(obj_from_json)
                .map(key => `${key}:${stringifyObjectWithNoQuotesOnKeys(obj_from_json[key])}`)
                .join(",")
            }}`;
};
Adel Bachène
la source
1
Vous devez également utiliser JSON.stringifypar exemple Date. Renvoie également 'null'si obj_from_jsonest nul.
Far Dmitry
1
Je viens de corriger les points soulevés par @FarDmitry en changeant la deuxième condition if pour qu'elle ressemble à ceci:if(typeof obj_from_json !== "object" || obj_from_json instanceof Date || obj_from_json === null)
Brunno Vodola Martins
2

Utilisation JSON5.stringify

JSON5 est un sur-ensemble de JSON qui autorise la syntaxe ES5, y compris les clés de propriété sans guillemets . L'implémentation de référence JSON5 ( json5package npm ) fournit un JSON5objet qui a les mêmes méthodes avec les mêmes arguments et sémantique que l' JSONobjet intégré .

Il est fort probable que le service que vous utilisez utilise cette bibliothèque.

Inigo
la source