Ciąg do obiektu w JS

190

Mam ciąg jako

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

teraz potrzebuję jednego obiektu obj takiego

obj = {firstName:name1, lastName:last1}

Jak mogę to zrobić w JS?

Rahul
źródło
1
Czy wartości ciągów name1 i last1 lub identyfikatory zdefiniowane gdzie indziej?
cdleary
1
Potrzebujesz więcej danych ... co robisz, jeśli klucz lub wartość zawierają przecinek lub dwukropek?
Brad
Jeśli Twój łańcuch nie ma formatu JSON, być może będziesz musiał użyć RegEp do obsługi.
bitfishxyz

Odpowiedzi:

165

W rzeczywistości najlepszym rozwiązaniem jest użycie JSON:

Dokumentacja

JSON.parse(text[, reviver]);

Przykłady:

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) Przekazywanie funkcji do 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
źródło
nie zezwala jednak na przekazywanie funkcji
K2xL
2
To prawda, jednak ściśle mówiąc, funkcje nie powinny znajdować się w żadnych obiektach JSON. Do tego masz RPC itp., Lub jeśli chcesz, możesz przekazać prototyp funkcji do json i zrobić to evalpóźniej.
Matej
36
Obawiam się, że w ogóle nie odpowiada na pytanie. Byłoby wspaniale, gdyby wszyscy zmienili pytanie, aby dopasować odpowiedź. Jak analizujesz „firstName: name1, lastName: last1”? nie „{„ hello ”:„ world ”}”.
Noel Abrahams,
33
To nie odpowiada na pytanie, nie wiem, dlaczego ma tak wiele pozytywnych opinii. Pytający (i ja, przejrzałem i skończyłem tutaj) ma dane, które nie są zgodne ze standardem JSON, więc nie można ich przeanalizować.
Aaron Greenwald
1
var a = "firstName: name1, lastName: last1"; JSON.parse ('{' + a + '}') zgłasza błąd.
Aaron Greenwald
73

Twój sznurek wygląda jak sznurek JSON bez nawiasów klamrowych.

Powinno to wtedy działać:

obj = eval('({' + str + '})');
Philippe Leybaert
źródło
22
jest to potencjalna dziura w bezpieczeństwie. W ogóle nie poleciłbym tej metody.
Breton
65
To zależy, skąd pochodzą dane. Niektórzy ludzie mają obsesję na punkcie bezpieczeństwa. Jeśli kontrolujesz swoje dane, możesz być bardziej pragmatyczny w poszukiwaniu rozwiązań. A może masz drzwi antywłamaniowe między kuchnią a salonem?
Philippe Leybaert
13
To nie działa Daje ci błąd „SyntaxError: Nieoczekiwany token:” . Sprawdziłem to w Chrome.
Nyambaa
12
Rozwiązanie wymaga nawiasów wokół:obj = eval('({' + str + '})');
Christian
12
prc322: Wszyscy wiedzą, że eval () nie jest zbyt bezpieczne (i to mało powiedziane), ale użycie eval () jest jedyną prawidłową odpowiedzią na powyższe pytanie, ponieważ łańcuch wejściowy nie jest prawidłowym łańcuchem JSON ORAZ łańcuch wejściowy odwołuje się do innych zmiennych wg nazwy.
Philippe Leybaert
50

Jeśli dobrze rozumiem:

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

Zakładam, że nazwa właściwości znajduje się po lewej stronie dwukropka, a wartość ciągu, który przyjmuje, jest po prawej stronie.

Pamiętaj, że Array.forEachjest to JavaScript 1.6 - możesz użyć zestawu narzędzi dla maksymalnej kompatybilności.

cdleary
źródło
Hej Cdleary ... Zastanawiam się, czy możesz mi pomóc: stackoverflow.com/questions/9357320/... Przepraszam, nie mogłem znaleźć innego sposobu na skontaktowanie się z Tobą oprócz komentarzy do twoich odpowiedzi
Jason
1
Idealny post. Ta odpowiedź korzysta z podstaw JavaScript w sposób bardzo przejrzysty i dlatego powinna działać w każdej przeglądarce
Michel
Uznałem, że takie podejście było dobrym początkiem, więc odpowiedź była przydatna. Jednak xecute ma prawidłowy punkt, że nie akceptuje ciągów znaków, które mogą zawierać przecinki. Możesz pójść ścieżką dopasowywania cudzysłowów, jeśli Twój kod musi obsługiwać ciągi przecinkami, ale nadal nie wychwytujesz przypadków, w których cudzysłowy są zastępowane. Po prostu zaimplementuj to, czego potrzebujesz, a następnie przestań.
Patrick Graham,
1
W kwestii analizy tekstu przedstawionego przez pytającego pytania „co jeśli” są bezużyteczne. Możemy zaakceptować podany przykład jako reprezentatywny dla danych lub po prostu nie możemy odpowiedzieć, ponieważ zawsze może istnieć „co, jeśli” , które przerwie parsowanie.
1
Plakat zapytał, jak przeanalizować niezwykłą strukturę, która nie była poprawna JSON. @cdleary bardzo dobrze odpowiedział na pytanie w danych okolicznościach. xecute: obsługa przecinka w ciągu wymagałaby mechanizmu cytowania lub zmiany znaczenia, wykraczającego poza zakres tej dyskusji.
Suncat2000,
37

Ten prosty sposób ...

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

wynik

name1
Wianto Wie
źródło
Głosuj za tym, ALTHOUGH: var string = "{firstName: 'name1', lastName: 'last1', f: function () {alert ('zostałeś zhakowany ...')} ()}"; eval ('var obj' + string) Może Cię to zhakować ... ale myślę, że warto, bo to jedyna rzecz, która działa w niektórych przypadkach.
Cody
12

jeśli używasz JQuery:

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

PAMIĘTAJ: eval jest złe! :RE

mzalazar
źródło
7
Używa jQuery eval. globalEval: /** code **/ window[ "eval" ].call( window, data ); /** more code **/
SomeShinyObject
12
Łańcuch podany przez właściciela pytania nie jest prawidłowym łańcuchem JSON. Tak więc ten kod jest bezużyteczny ...
Xecute
tak, eval nie jest zły, jeśli używasz go w „kontrolowanym” środowisku (cokolwiek poza danymi zewnętrznymi, takimi jak pliki, dane sieciowe lub dane wejściowe użytkownika, w takich przypadkach może być niebezpieczne, jeśli nie zostanie „przefiltrowane / zatwierdzone”).
mzalazar
11

Ponieważ metoda JSON.parse () wymaga, aby klucze Object były zawarte w cudzysłowach, aby działała poprawnie, musielibyśmy najpierw przekonwertować ciąg na łańcuch sformatowany w JSON przed wywołaniem metody 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

Działa to nawet wtedy, gdy ciąg znaków ma złożony obiekt (jak poniżej) i nadal będzie poprawnie konwertowany. Upewnij się tylko, że sam łańcuch jest ujęty w pojedyncze cudzysłowy.

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
źródło
10

Musisz użyć JSON.parse () do konwersji Ciągu na Obiekt:

var obj = JSON.parse('{ "firstName":"name1", "lastName": "last1" }');
Grigor IWT
źródło
9

Jeśli masz ciąg znaków foo: 1, bar: 2, możesz przekonwertować go na prawidłowy obiekt obj:

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

Podziękowania dla czarnucha w #javascript za to.

Aktualizacja z objaśnieniami:

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)

Mylące dokumenty MDN:

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

Funkcjonować:

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!
corysimmony
źródło
Jest to całkowicie niezrozumiała odpowiedź dla przeciętnego czytelnika. Czy możesz wyjaśnić, co robi każda linia? A jaki jest wynik końcowy, biorąc pod uwagę wkład OP?
not2qubit
1
Słusznie. Zazwyczaj nie mam czasu na opisywanie szczegółów i po prostu upuszczam pomocne fragmenty (szczególnie w przypadku, gdy natknę się na to samo dokładne pytanie SO po drodze), ale mam problem.
corysimmons
2
Jest to zdecydowanie najbardziej eleganckie rozwiązanie i faktycznie odpowiada na pytanie w przeciwieństwie do wielu innych odpowiedzi. I dziękuję za wyjaśnienie!
Cathy Ha
4

Zaimplementowałem rozwiązanie w kilku liniach kodu, które działa całkiem niezawodnie.

Posiadanie takiego elementu HTML, w którym chcę przekazać niestandardowe opcje:

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

funkcja analizuje opcje niestandardowe i zwraca obiekt, aby go gdzieś użył:

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
źródło
+1 za użycie data-atrybutu zamiast tworzenia pseudo-niestandardowych atrybutów, takich jak niektóre frameworki / biblioteki.
Jan
3
string = "firstName:name1, lastName:last1";

To zadziała:

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

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

Jednak to nie jest wydajne. Co się dzieje, gdy masz coś takiego:

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

split()podzieli „http”. Sugeruję więc użycie specjalnego separatora, takiego jak potok

 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
źródło
2
Po prostu pomysł - zamiast drugiego podziału („:”) możesz podzielić ciąg „ręcznie” według PIERWSZEGO dwukropka za pomocą indexOf („:”) i uważać część ciągu do tego dwukropka za klucz, a resztę po dwukropku za wartość.
Ondrej Vencovsky
2

To jest uniwersalny kod, bez względu na to, jak długo dane wejściowe są długie, ale w tym samym schemacie, jeśli istnieje: separator :)

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
źródło
2

Używam JSON5 i działa całkiem dobrze.

Zaletą jest to, że nie zawiera evali nie new Function , bardzo bezpieczny w użyciu.

James Yang
źródło
0

W Twoim przypadku

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
źródło
3
eval należy unikać za wszelką cenę.
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);
        });

Tablica „obiektów” będzie zawierała wszystkie obiekty

comeOnGetIt
źródło
0

Wiem, że to stary post, ale nie widziałem poprawnej odpowiedzi na pytanie.

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

Teraz możesz użyć obj.firstNamei, obj.lastNameaby uzyskać wartości, tak jak normalnie z obiektem.

Raoul
źródło