Convertir la chaîne d'objet en JSON

165

Comment puis-je convertir une chaîne qui décrit un objet en une chaîne JSON à l'aide de JavaScript (ou jQuery)?

par exemple: Convertissez ceci ( PAS une chaîne JSON valide):

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }"

dans ceci:

str = '{ "hello": "world", "places": ["Africa", "America", "Asia", "Australia"] }'

J'adorerais éviter d'utiliser eval()si possible.

snorpey
la source
Pourquoi votre chaîne n'est-elle pas JSON valide en premier lieu? Comment générez-vous cela?
Rocket Hazmat
2
La chaîne est stockée dans un data-attrubute, comme ceci: <div data-object="{hello:'world'}"></div>et je ne veux pas utiliser de guillemets simples dans le HTML (donc il n'est probablement pas digne de confiance)
snorpey
5
@snorpey: <div data-object='{"hello":"world"}'></div>est du HTML 100% valide (qu'est-ce que les guillemets simples ont à voir avec le fait de lui faire confiance ou non?). Si vous le faites de cette façon, vous pouvez le faire JSON.parseet cela fonctionnera bien. Remarque: les clés doivent également être citées.
Rocket Hazmat
@Rocket merci pour vos efforts! Je voulais juste trouver un moyen de ne pas avoir à utiliser des guillemets simples en HTML (même si c'est 100% valide) et en notation JSON.
snorpey
@snorpey: Il ne s'agit pas de mettre JSON dans un attribut HTML en premier lieu. Je suppose que vous pouvez utiliser des guillemets doubles et échapper à ceux du JSON <div data-object="{\"hello\":\"world\"}"></div>. Si vous ne souhaitez pas utiliser de JSON valide dans l'attribut, vous devrez créer votre propre format et l'analyser vous-même.
Rocket Hazmat

Réponses:

181

Si la chaîne est d'une source fiable , vous pouvez utiliser evalensuite JSON.stringifyle résultat. Comme ça:

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
var json = JSON.stringify(eval("(" + str + ")"));

Notez que lorsque vous eval utilisez un objet littéral, il doit être placé entre parenthèses, sinon les accolades sont analysées comme un bloc au lieu d'un objet.

Je suis également d'accord avec les commentaires sous la question qu'il serait bien préférable de simplement encoder l'objet dans un JSON valide pour commencer et éviter d'avoir à l'analyser, l'encoder, puis vraisemblablement l'analyser à nouveau . HTML prend en charge les attributs entre guillemets simples (assurez-vous simplement d'encoder en HTML les guillemets simples à l'intérieur des chaînes).

Matthew Crumley
la source
cela n'a pas de sens, si la chaîne provient d'une source fiable, pourquoi nous la convertissons à la place, nous la transformons en json valide.
allenhwkim
2
@allenhwkim L'idée est de convertir un JSON invalide en JSON valide, donc evalconvertit la chaîne en un objet JavaScript (ce qui fonctionne, tant que la chaîne représente du JavaScript valide, même si ce n'est pas du JSON valide). Puis JSON.stringifyconvertit à partir d'un objet en une chaîne JSON (valide). L'appel evalest dangereux si la chaîne ne provient pas d'une source fiable, car elle pourrait littéralement exécuter n'importe quel JavaScript, ce qui ouvre la possibilité d'attaques de script intersite.
Matthew Crumley
2
eval fera toujours de mauvaises choses dans ce cas si la chaîne est construite, par exemple, comme ceci: var str = "(function () {console.log (\" bad \ ")}) ()";
Rondo
Utiliser eval () exécutera le code JS. Il peut être facilement abusé.
FisNaN
@allenhwkim: nous ne faisons confiance à aucune source. Faire confiance aux TI signifie vérifier, vérifier et vérifier à nouveau.
Laszlo Varga
110

Votre chaîne n'est pas JSON valide, donc JSON.parse(ou jQuery $.parseJSON) ne fonctionnera pas.

Une façon serait d'utiliser evalpour "analyser" le JSON "invalide", puis stringifypour le "convertir" en JSON valide.

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }"
str = JSON.stringify(eval('('+str+')'));

Je suggère qu'au lieu d'essayer de «réparer» votre JSON invalide, vous commencez par un JSON valide. Comment est strgénéré, il devrait être corrigé ici, avant qu'il ne soit généré, pas après.

EDIT : Vous avez dit (dans les commentaires) que cette chaîne est stockée dans un attribut de données:

<div data-object="{hello:'world'}"></div>

Je vous suggère de le réparer ici, donc ça peut juste être JSON.parsed. Tout d'abord, les clés et les valeurs doivent être entre guillemets. Cela devrait ressembler à (les attributs entre guillemets simples en HTML sont valides):

<div data-object='{"hello":"world"}'></div>

Maintenant, vous pouvez simplement utiliser JSON.parse(ou jQuery $.parseJSON).

var str = '{"hello":"world"}';
var obj = JSON.parse(str);
Fusée Hazmat
la source
49

jQuery.parseJSON

str = jQuery.parseJSON(str)

Éditer. Ceci est à condition que vous ayez une chaîne JSON valide

Fermier
la source
1
true J'ai vu la question de savoir comment convertir une chaîne JSON en objet
Farmor
43

Utilisez un code simple dans le lien ci-dessous:

http://msdn.microsoft.com/es-es/library/ie/cc836466%28v=vs.94%29.aspx

var jsontext = '{"firstname":"Jesper","surname":"Aaberg","phone":["555-0100","555-0120"]}';
var contact = JSON.parse(jsontext);

et inverser

var str = JSON.stringify(arr);
Ronald
la source
La conversion de jsontext en objet String via une nouvelle chaîne (jsontext) est probablement encore meilleure, pour la sécurité des types.
Analyse floue
@fuzzyanalysis: Non, les wrappers primitifs ne doivent jamais être utilisés.
Ry-
1
JSON.parse () devrait être la réponse acceptée ici, comme indiqué par @LouiseMcMahon
pixel 67
24

J'espère que cette petite fonction convertit une chaîne JSON invalide en une chaîne valide.

function JSONize(str) {
  return str
    // wrap keys without quote with valid double quote
    .replace(/([\$\w]+)\s*:/g, function(_, $1){return '"'+$1+'":'})    
    // replacing single quote wrapped ones to double quote 
    .replace(/'([^']+)'/g, function(_, $1){return '"'+$1+'"'})         
}

Résultat

var invalidJSON = "{ hello: 'world',foo:1,  bar  : '2', foo1: 1, _bar : 2, $2: 3, 'xxx': 5, \"fuz\": 4, places: ['Africa', 'America', 'Asia', 'Australia'] }"
JSON.parse(invalidJSON) 
//Result: Uncaught SyntaxError: Unexpected token h VM1058:2
JSON.parse(JSONize(invalidJSON)) 
//Result: Object {hello: "world", foo: 1, bar: "2", foo1: 1, _bar: 2…}
allenhwkim
la source
Nous essayons de désévaluer b en utilisant JSON.parse notre code et cela semble être une bonne solution. Nous allons encore devoir gérer le remplacement constant à la main, mais au moins cela permet de contenir ces cas.
ravemir
1
C'est presque parfait. Ne fonctionne pas lorsqu'il : est dans l'une des valeurs.
seler
9

À utiliser avec prudence (à cause de eval()):

function strToJson(str) {
  eval("var x = " + str + ";");
  return JSON.stringify(x);
}

appeler comme:

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
alert( strToJson(str) );
Tomalak
la source
3
À l'électeur anonyme. Je vous mets au défi de fournir une meilleure solution. A part cela, une raison pour le vote défavorable serait bien.
Tomalak
1
@Rocket: Vous vous trompez. a) eval()est la seule façon de le faire. b) J'ai averti l'OP à ce sujet. c) Regardez la réponse de Matthew Crumley et pensez à une meilleure explication. (Oh, et d) la déclaration eval()est mauvaise est un non-sens sous cette forme généralisée.)
Tomalak
2
@Rocket: Ah, malentendu là. Désolé, je pensais que le vote négatif était le vôtre. :)
Tomalak
1
@kuboslav: Cela fonctionne bien, l'avez-vous même testé? Il fait ce eval("var x = " + str + ";")qui est totalement valide JS. Vous n'avez pas besoin de faire var x = ({a:12}).
Rocket Hazmat
2
@kuboslav Cela ne fonctionne pas dans IE7 car IE7 n'a pas de support JSON natif. Il commencera à fonctionner dès que vous l'utiliserez json2.js. Ne soyez pas si heureux.
Tomalak
4

Avertissement: n'essayez pas cela à la maison, ou pour tout ce qui nécessite que d'autres développeurs vous prennent au sérieux:

JSON.stringify(eval('(' + str + ')'));

Là, je l'ai fait.
Essayez de ne pas le faire, l'évaluation est MAUVAISE pour vous. Comme indiqué ci-dessus, utilisez le shim JSON de Crockford pour les navigateurs plus anciens (IE7 et inférieurs)

Cette méthode nécessite que votre chaîne soit un javascript valide , qui sera converti en un objet javascript qui peut ensuite être sérialisé en JSON.

edit: corrigé comme Rocket l'a suggéré.

gonchuki
la source
Cela devrait être JSON.stringify(eval('('+str+')'));, non pas que je cautionne eval, mais sa chaîne n'est pas JSON valide et JSON.parsene fonctionne donc pas.
Rocket Hazmat
4

Je mets ma réponse pour quelqu'un qui s'intéresse à ce vieux fil.

J'ai créé l' analyseur de données HTML5 pour le plugin jQuery et la démo qui convertissent une chaîne JSON malformée en un objet JavaScript sans utiliser eval().

Il peut transmettre les attributs HTML5 data- * ci-dessous:

<div data-object='{"hello":"world"}'></div>
<div data-object="{hello:'world'}"></div>
<div data-object="hello:world"></div>

dans l'objet:

{
    hello: "world"
}
tokkonopapa
la source
2

Il existe un moyen beaucoup plus simple d'accomplir cet exploit, il suffit de détourner l'attribut onclick d'un élément factice pour forcer le retour de votre chaîne en tant qu'objet JavaScript:

var jsonify = (function(div){
  return function(json){
    div.setAttribute('onclick', 'this.__json__ = ' + json);
    div.click();
    return div.__json__;
  }
})(document.createElement('div'));

// Let's say you had a string like '{ one: 1 }' (malformed, a key without quotes)
// jsonify('{ one: 1 }') will output a good ol' JS object ;)

Voici une démo: http://codepen.io/csuwldcat/pen/dfzsu (ouvrez votre console)

csuwldcat
la source
2

Vous devez utiliser "eval" puis JSON.stringify puis JSON.parse pour le résultat.

 var errorString= "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
 var jsonValidString = JSON.stringify(eval("(" + errorString+ ")"));
 var JSONObj=JSON.parse(jsonValidString);

entrez la description de l'image ici

Negi Rox
la source
1

Vous devez écrire des crochets, car sans eux, evalle code entre accolades sera considéré comme un bloc de commandes.

var i = eval("({ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] })");
kuboslav
la source
1

Votre meilleur pari et le plus sûr serait JSON5 - JSON for Humans . Il est créé spécifiquement pour ce cas d'utilisation.

const result = JSON5.parse("{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }");

console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/json5/0.5.1/json5.min.js"></script>

dereli
la source
1

L'utilisation de new Function () est meilleure que eval, mais ne doit être utilisée qu'avec une entrée sûre.

const parseJSON = obj => Function('"use strict";return (' + obj + ')')();

console.log(parseJSON("{a:(4-1), b:function(){}, c:new Date()}"))
// outputs: Object { a: 3, b: b(), c: Date 2019-06-05T09:55:11.777Z }

Sources: MDN , 2ality

SamGoody
la source
0

Pour votre exemple simple ci-dessus, vous pouvez le faire en utilisant 2 simples remplacements de regex:

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
str.replace(/(\w+):/g, '"$1":').replace(/'/g, '"');
 => '{ "hello": "world", "places": ["Africa", "America", "Asia", "Australia"] }'

Grande mise en garde : cette approche naïve suppose que l'objet n'a pas de chaînes contenant un caractère 'ou :. Par exemple, je ne peux pas penser à un bon moyen de convertir la chaîne d'objet suivante en JSON sans utiliser eval:

"{ hello: 'world', places: [\"America: The Progressive's Nightmare\"] }"
Topher Hunt
la source
0

Juste pour les bizarreries, vous pouvez convertir votre chaîne via babel-standalone

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";

function toJSON() {
  return {
    visitor: {
      Identifier(path) {
        path.node.name = '"' + path.node.name + '"'
      },
      StringLiteral(path) {
        delete path.node.extra
      }
    }
  }
}
Babel.registerPlugin('toJSON', toJSON);
var parsed = Babel.transform('(' + str + ')', {
  plugins: ['toJSON']
});
var json = parsed.code.slice(1, -2)
console.log(JSON.parse(json))
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

Moritz Roessler
la source
0

var str = "{bonjour: 'monde', lieux: ['Afrique', 'Amérique', 'Asie', 'Australie']}" var fStr = str .replace (/ ([Az] *) (:) / g, '"$ 1":') .replace (/ '/ g, "\" ")

console.log (JSON.parse (fStr))entrez la description de l'image ici

Désolé je suis sur mon téléphone, voici une photo.

Francis Leigh
la source
0

Une solution avec une expression régulière et n'utilisant pas eval:

str.replace(/([\s\S]*?)(')(.+?)(')([\s\S]*?)/g, "$1\"$3\"$5")

Je crois que cela devrait fonctionner pour plusieurs lignes et toutes les occurrences possibles (/ g flag) de "chaîne" à guillemets simples remplacés par "chaîne".

Chaitanya P
la source
0
var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
var json = JSON.stringify(eval("(" + str + ")"));
Praveenkumar
la source
-1

Vous devez peut-être essayer ceci:

str = jQuery.parseJSON(str)
howdyhyber
la source
Question spécifiée "ou jQuery" et c'est la solution parfaite si vous l'avez disponible.
Ecropolis