JavaScript에서 JSON 문자열을 특정 객체 프로토 타입으로 구문 분석


173

JSON 문자열을 구문 분석하여 JavaScript 객체로 변환하는 방법을 알고 있습니다. JSON.parse()최신 브라우저 (및 IE9 +)에서 사용할 수 있습니다 .

훌륭하지만 JavaScript 객체를 특정 JavaScript 객체 (예 : 특정 프로토 타입)로 전환하려면 어떻게해야합니까?

예를 들어, 다음이 있다고 가정하십시오.

function Foo()
{
   this.a = 3;
   this.b = 2;
   this.test = function() {return this.a*this.b;};
}
var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse({"a":4, "b": 3});
//Something to convert fooJSON into a Foo Object
//....... (this is what I am missing)
alert(fooJSON.test() ); //Prints 12

다시 한 번 JSON 문자열을 일반 JavaScript 객체로 변환하는 방법이 궁금하지 않습니다. JSON 문자열을 "Foo"개체로 변환하는 방법을 알고 싶습니다. 즉, 내 객체에는 이제 함수 'test'와 속성 'a'와 'b'가 있어야합니다.

업데이트 약간의 연구를 한 후, 나는 이것을 생각했습니다 ...

Object.cast = function cast(rawObj, constructor)
{
    var obj = new constructor();
    for(var i in rawObj)
        obj[i] = rawObj[i];
    return obj;
}
var fooJSON = Object.cast({"a":4, "b": 3}, Foo);

작동합니까?

업데이트 2017 년 5 월 :이 작업을 수행하는 "현대적인"방법은 via Object.assign이지만 IE 11 또는 이전 Android 브라우저에서는이 기능을 사용할 수 없습니다.


답변:


124

현재 답변에는 많은 수작업 또는 라이브러리 코드가 포함되어 있습니다. 필요하지 않습니다.

  1. JSON.parse('{"a":1}')일반 객체를 만드는 데 사용 합니다.

  2. 표준화 된 기능 중 하나를 사용하여 프로토 타입을 설정하십시오.

    • Object.assign(new Foo, { a: 1 })
    • Object.setPrototypeOf({ a: 1 }, Foo.prototype)

2
IE 및 이전 Android 브라우저를 포함한 이전 브라우저에서는 Object.assign을 사용할 수 없습니다. kangax.github.io/compat-table/es6/…
BMiner

5
을 사용하지 말아야한다는 큰 경고도 있습니다 Object.setPrototypeOf(...). developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
christo8989

@SimonEpskamp 해당 코드가 작동하지 않습니다. setPrototypeOf속성 설명 자인 두 번째 매개 변수 인 url을 확인하십시오 .
에릭 반 Velzen

6
프로토 타입이 필요한 속성이 있으면 프로토 타입을 설정 한 솔루션이 작동하지 않습니다. 즉, 첫 번째 수준의 데이터 계층 구조 만 해결합니다.
Vojta

2
속성을 자동으로 해결할 수있는 Object.assign (..)을 재귀 적으로 적용하는 아래의 솔루션을 미리 확인하십시오 (사전 제공된 약간의 정보 포함)
vir us

73

아래 예를 참조하십시오 (이 예는 기본 JSON 객체를 사용함). 변경 사항은 CAPITALS에 주석 처리됩니다.

function Foo(obj) // CONSTRUCTOR CAN BE OVERLOADED WITH AN OBJECT
{
    this.a = 3;
    this.b = 2;
    this.test = function() {return this.a*this.b;};

    // IF AN OBJECT WAS PASSED THEN INITIALISE PROPERTIES FROM THAT OBJECT
    for (var prop in obj) this[prop] = obj[prop];
}

var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6

// INITIALISE A NEW FOO AND PASS THE PARSED JSON OBJECT TO IT
var fooJSON = new Foo(JSON.parse('{"a":4,"b":3}'));

alert(fooJSON.test() ); //Prints 12

나는 당신이 이것의 "반대"를 할 수 있다고 생각합니다. 빈 Foo Object를 생성하고 fooJSON에서 새 Foo Object로 속성을 복사합니다. 마지막으로 fooJSON을 Foo Object를 가리 키도록 설정하십시오.
BMiner

8
이것은 매우 위험합니다. obj에 Foo 정의에없는 속성이있는 경우 이름을 모르는 숨겨진 속성이 추가 된 Foo 객체를 만듭니다. 루프 대신 간단히 수행 할 것입니다 : this.a = obj. a와 this.b = obj.b. 또는 직접 "a"와 "b"를 매개 변수로 전달합니다. new Foo (obj.a, obj.b)
Gabriel Llamas

2
GagleKas의 조언은들을 가치가 있습니다. ( "매우 위험한"것은 약간 OTT이지만) 위의 예는 아이디어를 제공하기위한 것입니다. 올바른 구현은 응용 프로그램에 따라 다릅니다.
Oliver Moran

11
프로토 타입 속성으로부터 자신을 보호 할 수 있습니다. for (var prop in obj) {if (obj.hasOwnProperty(prop)) {this[prop] = obj[prop];}}
Romain Vergnory

3
@RomainVergnory보다 안전을 위해 생성자에서 생성 된 속성 만 초기화합니다 (obj : 대신) for (var prop in obj) {if (this.hasOwnProperty(prop)) {this[prop] = obj[prop];}}. 이것은 서버가 모든 속성을 채울 것으로 예상하고 obj.hasOwnProperty ()가 실패하면 IMO도 처리해야한다고 가정합니다.
tekHedd

42

JSON 직렬화 / 직렬화 기능을 추가 하시겠습니까? 그런 다음 이것을보십시오 :

당신 은 이것을 달성하고 싶습니다 :

UML

toJson ()은 일반적인 메소드입니다.
fromJson ()은 정적 메소드입니다.

구현 :

var Book = function (title, author, isbn, price, stock){
    this.title = title;
    this.author = author;
    this.isbn = isbn;
    this.price = price;
    this.stock = stock;

    this.toJson = function (){
        return ("{" +
            "\"title\":\"" + this.title + "\"," +
            "\"author\":\"" + this.author + "\"," +
            "\"isbn\":\"" + this.isbn + "\"," +
            "\"price\":" + this.price + "," +
            "\"stock\":" + this.stock +
        "}");
    };
};

Book.fromJson = function (json){
    var obj = JSON.parse (json);
    return new Book (obj.title, obj.author, obj.isbn, obj.price, obj.stock);
};

사용법 :

var book = new Book ("t", "a", "i", 10, 10);
var json = book.toJson ();
alert (json); //prints: {"title":"t","author":"a","isbn":"i","price":10,"stock":10}

var book = Book.fromJson (json);
alert (book.title); //prints: t

참고 : 당신은 당신이 같은 모든 속성 정의 변경하려는 경우 this.title, this.author등으로 var title, var author, 등을하고 UML 정의를 달성하기 위해 그들에게 게터를 추가 할 수 있습니다.


4
나는 동의한다. 이 구현은 확실히 작동하고 훌륭합니다 ... 조금 장황하고 Book Object에만 해당됩니다. IMHO, JS의 힘은 프로토 타입과 원한다면 추가 속성을 가질 수있는 능력에서 비롯됩니다. 그것이 내가 말하는 전부입니다. 나는 실제로 하나의 라이너를 찾고있었습니다. x .__ proto__ = X.prototype; (현재 IE 브라우저와 호환되지는 않지만)
BMiner

4
toJson()개별 속성이 하드 코딩되었는지 또는 각각에 대한 속성을 사용하는지에 관계없이 메서드는 각 문자열 속성에있을 수있는 일부 문자에 대해 백 슬래시 이스케이프 코드를 추가해야 한다는 것을 잊지 마십시오 . (예를 들어 책 제목에는 따옴표가있을 수 있습니다.)
nnnnnn

1
그래, 나도 알아, 내 대답은 예와 질문에 대한 최고의 대답은했지만, 내가 다른 사람 돕는 내 시간을 낭비하는 이유 ... 심지어 긍정적 인 점 ... 나도 몰라
가브리엘 라마를

7
요즘 JSON.stringify()에는 JS에 직접 쓰는 대신 사용할 것입니다. 모든 최신 브라우저가 지원하기 때문에 이제 바퀴를 다시 만들 필요가 없습니다.
stone

2
@skypecakes와 동의합니다. 특성의 서브 세트 만 직렬화하려면 직렬화 가능 특성 상수를 작성하십시오. serializable = ['title', 'author', ...]. JSON.stringify(serializable.reduce((obj, prop) => {...obj, [prop]: this[prop]}, {}))
Atticus

19

유용한 블로그 게시물 : JavaScript 프로토 타입 이해

Object의 __proto__ 속성을 망칠 수 있습니다.

var fooJSON = jQuery.parseJSON({"a":4, "b": 3});
fooJSON.__proto__ = Foo.prototype;

이를 통해 fooJSON은 Foo 프로토 타입을 상속 할 수 있습니다.

비록 이것이 적어도 내가 읽은 것에서 IE에서 작동하지 않는다고 생각합니다.


2
사실, 그와 같은 것이 나의 첫 본능이었습니다.
Oliver Moran

14
참고 __proto__오래되었습니다 되지 않습니다 . 또한 성능상의 이유로 이미 생성 된 개체의 [[Prototype]] 내부 속성을 설정 __proto__하거나 다른 방법 으로 수정하지 않는 것이 좋습니다 .
Yu Asakusa

1
아아, 실제로 사용되지 않는 솔루션 중 어느 것도 이것보다 훨씬 더 복잡한 것은 없습니다.
Wim Leers

변경 성능을 테스트 한 결과 [[prototype]]Chrome과 관련이없는 것 같습니다. 파이어 폭스에서 new를 호출 하면 프로토 타입을 사용하는 것보다 느리고 Object.create가 가장 빠릅니다. FF의 문제는 첫 번째 테스트가 마지막 테스트보다 느리다는 것입니다. 실행 순서가 중요합니다. 크롬에서는 모든 것이 거의 같은 속도로 실행됩니다. 나는 속성 접근과 메소드 호출을 의미한다. 크레아틴은 새로운 것으로 더 빠르지 만 그렇게 중요하지는 않습니다. 참조 : jsperf.com/prototype-change-test-8874874/1 및 : jsperf.com/prototype-changed-method-call
Bogdan Mart

4
요즘 Object.setPrototypeOf(fooJSON, Foo.prototype)에는 설정하는 대신 전화를 걸 것입니다 fooJSON.__proto__... 맞습니까?
stakx-더 이상

11

질문에 뭔가 빠졌거나 다른 사람이 왜 reviver매개 변수를 언급하지 않았습니까?JSON.parse 2011 년 이후 무엇입니까?

다음은 작동하는 솔루션에 대한 간단한 코드입니다. https://jsfiddle.net/Ldr2utrr/

function Foo()
{
   this.a = 3;
   this.b = 2;
   this.test = function() {return this.a*this.b;};
}


var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse(`{"a":4, "b": 3}`, function(key,value){
if(key!=="") return value; //logic of course should be more complex for handling nested objects etc.
  let res = new Foo();
  res.a = value.a;
  res.b = value.b;
  return res;
});
// Here you already get Foo object back
alert(fooJSON.test() ); //Prints 12

추신 : 귀하의 질문은 혼란 스럽습니다 : >> 훌륭하지만 JavaScript 객체를 가져 와서 특정 JavaScript 객체 (예 : 특정 프로토 타입)로 바꾸는 방법은 무엇입니까? JSON 구문 분석에 대해 묻는 제목과 모순되지만 인용 된 단락은 JS 런타임 객체 프로토 타입 교체에 대해 묻습니다.


3

다른 접근법은을 사용할 수 있습니다 Object.create. 첫 번째 인수로 프로토 타입을 전달하고 두 번째 인수에서는 특성 이름 맵을 설명자에 전달합니다.

function SomeConstructor() {
  
};

SomeConstructor.prototype = {
  doStuff: function() {
      console.log("Some stuff"); 
  }
};

var jsonText = '{ "text": "hello wrold" }';
var deserialized = JSON.parse(jsonText);

// This will build a property to descriptor map
// required for #2 argument of Object.create
var descriptors = Object.keys(deserialized)
  .reduce(function(result, property) {
    result[property] = Object.getOwnPropertyDescriptor(deserialized, property);
  }, {});

var obj = Object.create(SomeConstructor.prototype, descriptors);


3

선택적 인수를 생성자에 추가하고을 호출 Object.assign(this, obj)한 다음 객체 또는 객체 배열 자체의 속성을 처리하는 것이 좋습니다.

constructor(obj) {
    if (obj != null) {
        Object.assign(this, obj);
        if (this.ingredients != null) {
            this.ingredients = this.ingredients.map(x => new Ingredient(x));
        }
    }
}

2

완벽을 기하기 위해 다음과 같은 간단한 단일 라이너가 있습니다 (Foo 이외의 속성을 확인할 필요가 없습니다).

var Foo = function(){ this.bar = 1; };

// angular version
var foo = angular.extend(new Foo(), angular.fromJson('{ "bar" : 2 }'));

// jquery version
var foo = jQuery.extend(new Foo(), jQuery.parseJSON('{ "bar" : 3 }'));

2

json-dry 라는 패키지를 만들었습니다. . (원형) 참조 및 클래스 인스턴스를 지원합니다.

클래스 toDry에서 프로토 타입과 unDry정적 메서드로 2 개의 새로운 메서드를 정의 하고 클래스 ( Dry.registerClass)를 등록한 다음 바로 시작해야합니다.


1

기술적으로 원하는 것은 아니지만, 처리하려는 객체의 유형을 미리 알고 있다면 알려진 객체의 프로토 타입의 호출 / 적용 방법을 사용할 수 있습니다.

당신은 이것을 바꿀 수 있습니다

alert(fooJSON.test() ); //Prints 12

이에

alert(Foo.prototype.test.call(fooJSON); //Prints 12

1

필자는 맞춤 객체와 모든 필드를 자동으로 구문 분석 할 수있는 일반적인 솔루션으로 찾아서 컴파일 할 수있는 솔루션을 결합하여 직렬화 해제 후 프로토 타입 메소드를 사용할 수 있습니다.

한 가지 가정은 자동으로 유형을 적용하려는 모든 객체에서 유형을 나타내는 특수 파일을 정의한 것입니다 ( this.__type예에서).

function Msg(data) {
    //... your init code
    this.data = data //can be another object or an array of objects of custom types. 
                     //If those objects defines `this.__type', their types will be assigned automatically as well
    this.__type = "Msg"; // <- store the object's type to assign it automatically
}

Msg.prototype = {
    createErrorMsg: function(errorMsg){
        return new Msg(0, null, errorMsg)
    },
    isSuccess: function(){
        return this.errorMsg == null;
    }
}

용법:

var responseMsg = //json string of Msg object received;
responseMsg = assignType(responseMsg);

if(responseMsg.isSuccess()){ // isSuccess() is now available
      //furhter logic
      //...
}

타입 할당 기능

function assignType(object){
    if(object && typeof(object) === 'object' && window[object.__type]) {
        object = assignTypeRecursion(object.__type, object);
    }
    return object;
}

function assignTypeRecursion(type, object){
    for (var key in object) {
        if (object.hasOwnProperty(key)) {
            var obj = object[key];
            if(Array.isArray(obj)){
                 for(var i = 0; i < obj.length; ++i){
                     var arrItem = obj[i];
                     if(arrItem && typeof(arrItem) === 'object' && window[arrItem.__type]) {
                         obj[i] = assignTypeRecursion(arrItem.__type, arrItem);
                     }
                 }
            } else  if(obj && typeof(obj) === 'object' && window[obj.__type]) {
                object[key] = assignTypeRecursion(obj.__type, obj);
            }
        }
    }
    return Object.assign(new window[type](), object);
}

0

현재 받아 들여진 답변이 효과가 없었습니다. Object.assign ()을 올바르게 사용해야합니다.

class Person {
    constructor(name, age){
        this.name = name;
        this.age = age;
    }

    greet(){
        return `hello my name is ${ this.name } and i am ${ this.age } years old`;
    }
}

이 클래스의 객체를 정상적으로 생성합니다 :

let matt = new Person('matt', 12);
console.log(matt.greet()); // prints "hello my name is matt and i am 12 years old"

JSON 문자열이있는 경우 Person 클래스로 구문 분석해야합니다.

let str = '{"name": "john", "age": 15}';
let john = JSON.parse(str); // parses string into normal Object type

console.log(john.greet()); // error!!

john = Object.assign(Person.prototype, john); // now john is a Person type
console.log(john.greet()); // now this works

-3

Olivers의 대답은 매우 분명하지만, angular js에서 솔루션을 찾고 있다면 Angular-jsClass라는 멋진 모듈을 작성했습니다. 그러나 개발자가 BMiner가 말한 문제에 직면한다고 말하면서 JSON을 프로토 타입 또는 생성자 표기법 객체로 직렬화하는 방법

var jone = new Student();
jone.populate(jsonString); // populate Student class with Json string
console.log(jone.getName()); // Student Object is ready to use

https://github.com/imalhasaranga/Angular-JSClass

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.