String para objeto em JS

190

Eu tenho uma string como

string = "firstName:name1, lastName:last1"; 

agora eu preciso de um objeto obj tal que

obj = {firstName:name1, lastName:last1}

Como posso fazer isso em JS?

rahul
fonte
1
Os valores ou identificadores de string name1 e last1 foram definidos em outro lugar?
cdleary
1
Mais dados necessários ... o que você está fazendo se a chave ou o valor contiver uma vírgula ou dois pontos?
213 Brad
Se sua string não estiver no formato JSON, talvez seja necessário usar o RegEp para manipular.
bitfishxyz

Respostas:

165

Na verdade, a melhor solução é usar JSON:

Documentação

JSON.parse(text[, reviver]);

Exemplos:

1)

var myobj = JSON.parse('{ "hello":"world" }');
alert(myobj.hello); // 'world'

2)

var myobj = JSON.parse(JSON.stringify({
    hello: "world"
});
alert(myobj.hello); // 'world'

3) Passando uma função para JSON

var obj = {
    hello: "World",
    sayHello: (function() {
        console.log("I say Hello!");
    }).toString()
};
var myobj = JSON.parse(JSON.stringify(obj));
myobj.sayHello = new Function("return ("+myobj.sayHello+")")();
myobj.sayHello();
Matej
fonte
não permitirá que as funções sejam passadas embora
K2xL 4/14
2
Isso é verdade, no entanto, estritamente falando, as funções não devem estar em nenhum objeto JSON. Para isso, você tem RPC etc ou, se desejar, pode passar o protótipo de uma função para o json e fazer isso evalmais tarde.
Matej 4/14
36
Receio não responder à pergunta. Seria maravilhoso se todos mudassem a pergunta para se adequarem à resposta. Como você analisa "firstName: name1, lastName: last1"? não '{"olá": "mundo"}'.
Noel Abrahams
33
Isso não responde à pergunta, não sei por que há tantos votos positivos. O interlocutor (e eu, pesquisei no Google e acabei aqui) possui dados que não estão em conformidade com o padrão JSON e, portanto, não podem ser analisados.
Aaron Greenwald
1
var a = "nome: sobrenome1, sobrenome: sobrenome1"; JSON.parse ('{' + a + '}') gera um erro.
Aaron Greenwald
73

Sua sequência se parece com uma sequência JSON sem os chavetas.

Isso deve funcionar então:

obj = eval('({' + str + '})');
Philippe Leybaert
fonte
22
esse é um buraco de segurança em potencial. Eu não recomendaria esse método.
187 Breton
65
Depende de onde os dados vêm. Algumas pessoas são obcecadas por segurança. Se você controla seus dados, pode ser mais pragmático ao encontrar soluções. Ou você tem uma porta à prova de ladrões entre a cozinha e a sala?
Philippe Leybaert 07/07/2009
13
Isso não está funcionando. Ele fornece o erro 'SyntaxError: token inesperado:' . Eu verifiquei no Chrome.
Nyambaa
12
A solução precisa de parênteses em torno dela:obj = eval('({' + str + '})');
Christian
12
prc322: Todo mundo sabe que eval () não é muito seguro (e isso é um eufemismo), mas usar eval () é a única resposta correta para a pergunta acima porque a string de entrada não é uma string JSON válida E a string de entrada faz referência a outras variáveis pelo nome.
Philippe Leybaert 16/10
50

Se estou entendendo corretamente:

var properties = string.split(', ');
var obj = {};
properties.forEach(function(property) {
    var tup = property.split(':');
    obj[tup[0]] = tup[1];
});

Estou assumindo que o nome da propriedade está à esquerda dos dois pontos e o valor da string que assume é à direita.

Observe que Array.forEaché o JavaScript 1.6 - você pode usar um kit de ferramentas para obter compatibilidade máxima.

cdleary
fonte
Ei, Cdleary ... gostaria de saber se você pode me ajudar: stackoverflow.com/questions/9357320/… Desculpe, não foi possível encontrar outra maneira de entrar em contato com você, exceto por meio de comentários nas respostas
Jason
1
Post prefeito. Essa resposta usa os conceitos básicos de JavaScript de maneira muito clara e, portanto, deve funcionar em todos os navegadores
Michel
Achei que essa abordagem foi um bom começo, então a resposta foi útil. No entanto, xecute tem um ponto válido que não acomoda seqüências de caracteres que podem ter vírgulas. Você pode seguir o caminho das aspas correspondentes se seu código precisar manipular seqüências de caracteres com vírgulas, mas ainda assim não captará casos em que as aspas são escapadas. Basta implementar o que você precisa e depois parar.
Patrick Graham
1
Em uma questão de analisar o texto apresentado pelo solicitante, as perguntas do "e se" são inúteis. Podemos aceitar o exemplo fornecido como representante dos dados ou simplesmente não podemos responder, pois sempre pode haver um "e se" que interrompa a análise.
1
O pôster perguntou sobre como analisar uma estrutura incomum que não era JSON válida. @cdleary respondeu muito bem à pergunta, dadas as circunstâncias. xecute: manipular uma vírgula em uma string exigiria um mecanismo de citação ou escape, além do escopo desta discussão.
Suncat2000
37

Desta maneira simples ...

var string = "{firstName:'name1', lastName:'last1'}";
eval('var obj='+string);
alert(obj.firstName);

resultado

name1
Wianto Wie
fonte
Voto positivo para isso, EMBORA: var string = "{firstName: 'name1', lastName: 'last1', f: function () {alert ('você foi hackeado ...')} ()}"; eval ('var obj' + string) Isso pode te hackear ... mas acho que vale a pena porque é a única coisa que funciona em alguns casos.
Cody
12

se você estiver usando JQuery:

var obj = jQuery.parseJSON('{"path":"/img/filename.jpg"}');
console.log(obj.path); // will print /img/filename.jpg

LEMBRE-SE: eval é mau! : D

mzalazar
fonte
7
jQuery usa eval. globalEval: /** code **/ window[ "eval" ].call( window, data ); /** more code **/
SomeShinyObject
12
A cadeia, fornecida pelo proprietário da pergunta, não é uma cadeia JSON válida. Assim, este código é inútil ...
xecute
sim, eval não é ruim se você o usar em um ambiente "controlado" (qualquer coisa, exceto dados externos, como arquivos, rede ou entrada do usuário, nesses casos, pode ser perigoso se não for "filtrado / validado").
Mzalazar 16/09
11

Como o método JSON.parse () exige que as chaves Object sejam incluídas entre aspas para que funcione corretamente, primeiro precisamos converter a sequência em uma sequência formatada em JSON antes de chamar o método JSON.parse ().

var obj = '{ firstName:"John", lastName:"Doe" }';

var jsonStr = obj.replace(/(\w+:)|(\w+ :)/g, function(matchedStr) {
  return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":';
});

obj = JSON.parse(jsonStr); //converts to a regular object

console.log(obj.firstName); // expected output: John
console.log(obj.lastName); // expected output: Doe

Isso funcionaria mesmo se a string tivesse um objeto complexo (como o seguinte) e ainda assim fosse convertida corretamente. Apenas verifique se a cadeia de caracteres está entre aspas simples.

var strObj = '{ name:"John Doe", age:33, favorites:{ sports:["hoops", "baseball"], movies:["star wars", "taxi driver"]  }}';

var jsonStr = strObj.replace(/(\w+:)|(\w+ :)/g, function(s) {
  return '"' + s.substring(0, s.length-1) + '":';
});

var obj = JSON.parse(jsonStr);
console.log(obj.favorites.movies[0]); // expected output: star wars

Faisal Chishti
fonte
10

Você precisa usar JSON.parse () para converter String em um Objeto:

var obj = JSON.parse('{ "firstName":"name1", "lastName": "last1" }');
Grigor IWT
fonte
9

Se você tiver uma string como essa, foo: 1, bar: 2poderá convertê-la em um objeto válido com:

str
  .split(',')
  .map(x => x.split(':').map(y => y.trim()))
  .reduce((a, x) => {
    a[x[0]] = x[1];
    return a;
  }, {});

Graças a niggler em #javascript por isso.

Atualize com explicações:

const obj = 'foo: 1, bar: 2'
  .split(',') // split into ['foo: 1', 'bar: 2']
  .map(keyVal => { // go over each keyVal value in that array
    return keyVal
      .split(':') // split into ['foo', '1'] and on the next loop ['bar', '2']
      .map(_ => _.trim()) // loop over each value in each array and make sure it doesn't have trailing whitespace, the _ is irrelavent because i'm too lazy to think of a good var name for this
  })
  .reduce((accumulator, currentValue) => { // reduce() takes a func and a beginning object, we're making a fresh object
    accumulator[currentValue[0]] = currentValue[1]
    // accumulator starts at the beginning obj, in our case {}, and "accumulates" values to it
    // since reduce() works like map() in the sense it iterates over an array, and it can be chained upon things like map(),
    // first time through it would say "okay accumulator, accumulate currentValue[0] (which is 'foo') = currentValue[1] (which is '1')
    // so first time reduce runs, it starts with empty object {} and assigns {foo: '1'} to it
    // second time through, it "accumulates" {bar: '2'} to it. so now we have {foo: '1', bar: '2'}
    return accumulator
  }, {}) // when there are no more things in the array to iterate over, it returns the accumulated stuff

console.log(obj)

Documentos MDN confusos:

Demo: http://jsbin.com/hiduhijevu/edit?js,console

Função:

const str2obj = str => {
  return str
    .split(',')
    .map(keyVal => {
      return keyVal
        .split(':')
        .map(_ => _.trim())
    })
    .reduce((accumulator, currentValue) => {
      accumulator[currentValue[0]] = currentValue[1]
      return accumulator
    }, {})
}

console.log(str2obj('foo: 1, bar: 2')) // see? works!
corysimmons
fonte
Esta é uma resposta totalmente incompreensível para o leitor comum. Você pode explicar o que cada linha faz? E qual é a saída resultante, dada a entrada do OP?
Not2qubit
1
Justo. Normalmente, não tenho tempo para detalhar as coisas e estou apenas soltando alguns trechos úteis (especificamente no caso de eu encontrar a mesma pergunta SO exata no caminho), mas entendi.
Corysimmons
2
Esta é definitivamente a solução mais elegante e, na verdade, responde à pergunta, diferentemente de muitas outras respostas. E obrigado pela explicação!
Cathy Ha
4

Eu implementei uma solução em algumas linhas de código que funcionam de maneira bastante confiável.

Ter um elemento HTML como este onde eu quero passar opções personalizadas:

<div class="my-element"
    data-options="background-color: #dadada; custom-key: custom-value;">
</div>

uma função analisa as opções personalizadas e retorna um objeto para usá-lo em algum lugar:

function readCustomOptions($elem){
    var i, len, option, options, optionsObject = {};

    options = $elem.data('options');
    options = (options || '').replace(/\s/g,'').split(';');
    for (i = 0, len = options.length - 1; i < len; i++){
        option = options[i].split(':');
        optionsObject[option[0]] = option[1];
    }
    return optionsObject;
}

console.log(readCustomOptions($('.my-element')));
rodu
fonte
+1 por usar o data-atributo em vez de criar atributos pseudo-personalizados, como algumas estruturas / bibliotecas.
João
3
string = "firstName:name1, lastName:last1";

Isso funcionará:

var fields = string.split(', '),
    fieldObject = {};

if( typeof fields === 'object') ){
   fields.each(function(field) {
      var c = property.split(':');
      fieldObject[c[0]] = c[1];
   });
}

No entanto, não é eficiente. O que acontece quando você tem algo parecido com isto:

string = "firstName:name1, lastName:last1, profileUrl:http://localhost/site/profile/1";

split()irá dividir 'http'. Então eu sugiro que você use um delimitador especial como pipe

 string = "firstName|name1, lastName|last1";


   var fields = string.split(', '),
        fieldObject = {};

    if( typeof fields === 'object') ){
       fields.each(function(field) {
          var c = property.split('|');
          fieldObject[c[0]] = c[1];
       });
    }
Emeka Mbah
fonte
2
Apenas uma idéia - em vez da segunda divisão (':'), você pode dividir a sequência "manualmente" por PRIMEIRO dois pontos usando indexOf (":") e considerar a parte da sequência até esse dois pontos como chave e o restante após os dois pontos como O valor que.
Ondrej Vencovsky
2

Este é um código universal, não importa quanto sua entrada seja longa, mas no mesmo esquema, se houver: separador :)

var string = "firstName:name1, lastName:last1"; 
var pass = string.replace(',',':');
var arr = pass.split(':');
var empty = {};
arr.forEach(function(el,i){
  var b = i + 1, c = b/2, e = c.toString();
     if(e.indexOf('.') != -1 ) {
     empty[el] = arr[i+1];
  } 
}); 
  console.log(empty)
panatoni
fonte
2

Estou usando JSON5 , e funciona muito bem.

A parte boa é que ele contém não eval e não new Function , muito seguro de usar.

James Yang
fonte
0

No seu caso

var KeyVal = string.split(", ");
var obj = {};
var i;
for (i in KeyVal) {
    KeyVal[i] = KeyVal[i].split(":");
    obj[eval(KeyVal[i][0])] = eval(KeyVal[i][1]);
}
Vahag Chakhoyan
fonte
3
avaliação deve ser evitada a todo custo.
Alex
0
var stringExample = "firstName:name1, lastName:last1 | firstName:name2, lastName:last2";    

var initial_arr_objects = stringExample.split("|");
    var objects =[];
    initial_arr_objects.map((e) => {
          var string = e;
          var fields = string.split(','),fieldObject = {};
        if( typeof fields === 'object') {
           fields.forEach(function(field) {
              var c = field.split(':');
              fieldObject[c[0]] = c[1]; //use parseInt if integer wanted
           });
        }
            console.log(fieldObject)
            objects.push(fieldObject);
        });

A matriz "objects" terá todos os objetos

comeOnGetIt
fonte
0

Sei que este é um post antigo, mas não encontrei a resposta correta para a pergunta.

var jsonStrig = '{';
      var items = string.split(',');
      for (var i = 0; i < items.length; i++) {
          var current = items[i].split(':');
          jsonStrig += '"' + current[0] + '":"' + current[1] + '",';
      }
      jsonStrig = jsonStrig.substr(0, jsonStrig.length - 1);
      jsonStrig += '}';
var obj = JSON.parse(jsonStrig);
console.log(obj.firstName, obj.lastName);

Agora você pode usar obj.firstNamee obj.lastNameobter os valores como faria normalmente com um objeto.

Raoul
fonte