Как создать объект JSON для класса typescript

Я прочитал объект JSON с удаленного сервера REST. Этот объект JSON обладает всеми свойствами класса typescript (по дизайну). Как передать полученный JSON-объект типу var?

Я не хочу заполнять var typescript (т.е. иметь конструктор, который принимает этот объект JSON). Он большой и копирует все по суб-объекту по под-объекту и свойству по свойству занимает много времени.

Обновление: Вы можете передать его в интерфейс typescript!

Ответ 1

Вы не можете просто передать результат обычного старого JavaScript из запроса Ajax в экземпляр класса прототипа JavaScript/ TypeScript. Существует множество методов для этого и, как правило, связаны с копированием данных. Если вы не создадите экземпляр класса, он не будет иметь никаких методов или свойств. Он останется простым объектом JavaScript.

Хотя, если вы только имели дело с данными, вы могли бы просто сделать приведение к интерфейсу (поскольку это чисто структура времени компиляции), для этого потребуется, чтобы вы использовали класс TypeScript, который использует экземпляр данных и выполняет операции с этими данными.

Некоторые примеры копирования данных:

В сущности, вы просто:

var d = new MyRichObject();
d.copyInto(jsonResult);

Ответ 2

У меня была такая же проблема, и я нашел библиотеку, которая выполняет эту работу: https://github.com/pleerock/class-transformer.

Он работает следующим образом:

        let jsonObject = response.json() as Object;
        let fooInstance = plainToClass(Models.Foo, jsonObject);
        return fooInstance;

Он поддерживает вложенные дочерние элементы, но вы должны украсить свой член класса.

Ответ 3

В TypeScript вы можете сделать утверждение типа с помощью интерфейса и таких дженериков:

var json = Utilities.JSONLoader.loadFromFile("../docs/location_map.json");
var locations: Array<ILocationMap> = JSON.parse(json).location;

Где ILocationMap описывает форму ваших данных. Преимущество этого метода заключается в том, что ваш JSON может содержать больше свойств, но форма удовлетворяет условиям интерфейса.

Я надеюсь, что это поможет!

Ответ 4

Если вы используете ES6, попробуйте следующее:

class Client{
  name: string

  displayName(){
    console.log(this.name)
  }
}

service.getClientFromAPI().then(clientData => {

  // Here the client data from API only have the "name" field
  // If we want to use the Client class methods on this data object we need to:
  let clientWithType = Object.assign(new Client(), clientData)

  clientWithType.displayName()
})

Но этот путь не будет работать на объект гнезда, к сожалению.

Ответ 5

Я нашел очень интересную статью о родовом литье JSON для класса Typescript:

http://cloudmark.github.io/Json-Mapping/

В итоге вы получите следующий код:

let example = {
                "name": "Mark", 
                "surname": "Galea", 
                "age": 30, 
                "address": {
                  "first-line": "Some where", 
                  "second-line": "Over Here",
                  "city": "In This City"
                }
              };

MapUtils.deserialize(Person, example);  // custom class

Ответ 6

TL;DR: один вкладыш

// This assumes your constructor method will assign properties from the arg.
.map((instanceData: MyClass) => new MyClass(instanceData));

Подробный ответ

Я бы не рекомендовал подход Object.assign, поскольку он может ненадлежащим образом помещать ваш экземпляр класса нерелевантными свойствами (а также определенными закрытиями), которые не были объявлены внутри самого класса.

В классе, который вы пытаетесь десериализовать, я бы гарантировал, что будут определены любые свойства, которые вы хотите десериализовать (нуль, пустой массив и т.д.). Определив свои свойства с помощью начальных значений, вы обнаружите их видимость при попытке итерации членов класса для назначения значений (см. Ниже метод десериализации).

export class Person {
  public name: string = null;
  public favoriteSites: string[] = [];

  private age: number = null;
  private id: number = null;
  private active: boolean;

  constructor(instanceData?: Person) {
    if (instanceData) {
      this.deserialize(instanceData);
    }
  }

  private deserialize(instanceData: Person) {
    // Note this.active will not be listed in keys since it declared, but not defined
    const keys = Object.keys(this);

    for (const key of keys) {
      if (instanceData.hasOwnProperty(key)) {
        this[key] = instanceData[key];
      }
    }
  }
}

В приведенном выше примере я просто создал метод десериализации. В реальном мире я бы централизовал его в многоразовом базовом классе или методе обслуживания.

Вот как использовать это в чем-то вроде http resp...

this.http.get(ENDPOINT_URL)
  .map(res => res.json())
  .map((resp: Person) => new Person(resp) ) );

Если tslint/ide жалуется на то, что тип аргумента несовместим, просто введите аргумент в тот же тип, используя angular скобки <YourClassName>, например:

const person = new Person(<Person> { name: 'John', age: 35, id: 1 });

Если у вас есть члены класса определенного типа (например: экземпляр другого класса), вы можете включить их в строковые экземпляры с помощью методов getter/setter.

export class Person {
  private _acct: UserAcct = null;
  private _tasks: Task[] = [];

  // ctor & deserialize methods...

  public get acct(): UserAcct {
    return this.acct;
  }
  public set acct(acctData: UserAcct) {
    this._acct = new UserAcct(acctData);
  }

  public get tasks(): Task[] {
    return this._tasks;
  }

  public set tasks(taskData: Task[]) {
    this._tasks = taskData.map(task => new Task(task));
  }
}

В приведенном выше примере десериализуются как acct, так и список задач в соответствующие экземпляры классов.

Ответ 7

Предполагая, что json обладает теми же свойствами, что и ваш класс typescript, вам не нужно копировать свои свойства Json в ваш объект typescript. Вам просто нужно построить объект typescript, передающий json-данные в конструкторе.

В своем обратном вызове ajax вы получаете компанию:

onReceiveCompany( jsonCompany : any ) 
{
   let newCompany = new Company( jsonCompany );

   // call the methods on your newCompany object ...
}

In, чтобы сделать эту работу:

1) Добавьте конструктор в свой класс typescript, который принимает данные json в качестве параметра. В этом конструкторе вы расширяете свой json-объект с помощью jQuery, например: $.extend( this, jsonData). $.extend позволяет сохранять прототипы javascript при добавлении свойств объекта json.

2) Обратите внимание, что вам придется делать то же самое для связанных объектов. В случае Employees в этом примере вы также создаете конструктор, который принимает часть данных json для сотрудников. Вы вызываете $.map для перевода сотрудников json на объекты typescript Employee.

export class Company
{
    Employees : Employee[];

    constructor( jsonData: any )
    {
        $.extend( this, jsonData);

        if ( jsonData.Employees )
            this.Employees = $.map( jsonData.Employees , (emp) => {
                return new Employee ( emp );  });
    }
}

export class Employee
{
    name: string;
    salary: number;

    constructor( jsonData: any )
    {
        $.extend( this, jsonData);
    }
}

Это лучшее решение, которое я нашел при работе с typescript классами и объектами json.

Ответ 8

В моем случае это работает. Я использовал функции Object.assign(target, sources...). Во-первых, создание правильного объекта, затем копирует данные из объекта json в target.Example:

let u:User = new User();
Object.assign(u , jsonUsers);

И более продвинутый пример использования. Пример использования массива.

this.someService.getUsers().then((users: User[]) => {
  this.users = [];
  for (let i in users) {
    let u:User = new User();
    Object.assign(u , users[i]);
    this.users[i] = u;
    console.log("user:" + this.users[i].id);
    console.log("user id from function(test it work) :" + this.users[i].getId());
  }

});

export class User {
  id:number;
  name:string;
  fullname:string;
  email:string;

  public getId(){
    return this.id;
  }
}

Ответ 9

Автоматически проверять, есть ли у объекта JSON, полученного с сервера, ожидаемые (читай, соответствуют) свойства интерфейса машинописного текста, пока нечем. Но вы можете использовать Определяемые пользователем Стражи Типа

Учитывая следующий интерфейс и глупый объект json (это мог быть любой тип):

interface MyInterface {
    key: string;
 }

const json: object = { "key": "value" }

Три возможных способа:

A. Утверждение типа или простое статическое приведение после переменной

const myObject: MyInterface = json as MyInterface;

B. Простое статическое приведение, перед переменной и между бриллиантами

const myObject: MyInterface = <MyInterface>json;

C. Продвинутый динамический состав, вы сами проверяете структуру объекта

function isMyInterface(json: any): json is MyInterface {
    // silly condition to consider json as conform for MyInterface
    return typeof json.key === "string";
}

if (isMyInterface(json)) {
    console.log(json.key)
}
else {
        throw new Error('Expected MyInterface, got '${json}'.');
}

Вы можете json; //no error myObject.key; //custom type guard function isMyInterface(json: any): json is MyInterface { return json.key !== undefined; } if (isMyInterface(json)) { //no error, the type is inferred by our custom type guard //in short, we are sure json conforms to MyInterface at this point json.key; } else { //Fallback if the dynamic cast did not work throw new Error(`Expected MyInterface, got '${json}'.`); } rel=noreferrer>поиграть с этим примером здесь

Обратите внимание, что здесь трудность заключается в написании функции isMyInterface. Я надеюсь, что TS добавит декоратор рано или поздно, чтобы экспортировать сложную типизацию во время выполнения и позволить среде выполнения проверять структуру объекта при необходимости. На данный момент вы можете использовать валидатор схемы json, цель которого примерно такая же, ИЛИ этот генератор функций проверки типа во время выполнения.

Ответ 10

Пока это не кастинг как таковой; Я нашел https://github.com/JohnWhiteTB/TypedJSON полезной альтернативой.

@JsonObject
class Person {
    @JsonMember
    firstName: string;

    @JsonMember
    lastName: string;

    public getFullname() {
        return this.firstName + " " + this.lastName;
    }
}
var person = TypedJSON.parse('{ "firstName": "John", "lastName": "Doe" }', Person);

person instanceof Person; // true
person.getFullname(); // "John Doe"

Ответ 11

Вы можете создать interface вашего типа (SomeType) и привести объект в него.

const typedObject: SomeType = <SomeType> responseObject;

Ответ 12

Старый вопрос с в основном правильными, но не очень эффективными ответами. Вот что я предлагаю:

Создайте базовый класс, который содержит метод init() и статические методы приведения (для одного объекта и массива). Статические методы могут быть где угодно; версия с базовым классом и init() впоследствии позволяет легко расширяться.

export class ContentItem {
    // parameters: doc - plain JS object, proto - class we want to cast to (subclass of ContentItem)
    static castAs<T extends ContentItem>(doc: T, proto: typeof ContentItem): T {
        // if we already have the correct class skip the cast
        if (doc instanceof proto) { return doc; }
        // create a new object (create), and copy over all properties (assign)
        const d: T = Object.create(proto.prototype);
        Object.assign(d, doc);
        // reason to extend the base class - we want to be able to call init() after cast
        d.init(); 
        return d;
    }
    // another method casts an array
    static castAllAs<T extends ContentItem>(docs: T[], proto: typeof ContentItem): T[] {
        return docs.map(d => ContentItem.castAs(d, proto));
    }
    init() { }
}

Подобная механика (с assign()) была упомянута в посте @Adam111p. Просто еще один (более полный) способ сделать это. @Timothy Perez критически относится к assign(), но imho он здесь вполне уместен.

Реализуйте производный (настоящий) класс:

import { ContentItem } from './content-item';

export class SubjectArea extends ContentItem {
    id: number;
    title: string;
    areas: SubjectArea[]; // contains embedded objects
    depth: number;

    // method will be unavailable unless we use cast
    lead(): string {
        return '. '.repeat(this.depth);
    }

    // in case we have embedded objects, call cast on them here
    init() {
        if (this.areas) {
            this.areas = ContentItem.castAllAs(this.areas, SubjectArea);
        }
    }
}

Теперь мы можем привести объект, полученный из сервиса:

const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);

Вся иерархия объектов SubjectArea будет иметь правильный класс.

Вариант использования/пример; создайте сервис Angular (снова абстрактный базовый класс):

export abstract class BaseService<T extends ContentItem> {
  BASE_URL = 'http://host:port/';
  protected abstract http: Http;
  abstract path: string;
  abstract subClass: typeof ContentItem;

  cast(source: T): T {
    return ContentItem.castAs(source, this.subClass);
  }
  castAll(source: T[]): T[] {
    return ContentItem.castAllAs(source, this.subClass);
  }

  constructor() { }

  get(): Promise<T[]> {
    const value = this.http.get('${this.BASE_URL}${this.path}')
      .toPromise()
      .then(response => {
        const items: T[] = this.castAll(response.json());
        return items;
      });
    return value;
  }
}

Использование становится очень простым; создать сервис Area:

@Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
  path = 'area';
  subClass = SubjectArea;

  constructor(protected http: Http) { super(); }
}

Метод get() службы вернет Promise для массива, уже приведенного к объектам SubjectArea (вся иерархия)

Теперь скажите, у нас есть другой класс:

export class OtherItem extends ContentItem {...}

Создать сервис, который извлекает данные и приводит к правильному классу, так же просто, как:

@Injectable()
export class OtherItemService extends BaseService<OtherItem> {
  path = 'other';
  subClass = OtherItem;

  constructor(protected http: Http) { super(); }
}

Ответ 13

Я создал простой инструмент для этого https://beshanoe.github.io/json2ts/ В отличие от json2ts.com, он работает непосредственно в браузере и не отправляет ваши данные на неизвестные серверы. Также он имеет несколько настроек. Я буду работать над улучшением его функциональности

Ответ 14

Одна вещь, которую мы сделали, потому что мы являемся магазином .NET, создаем этот инструмент (https://github.com/Centeva/TypeScripter).

Он строит классы typescript из наших dll. Все, что нам нужно сделать, это передать наш ответ json в класс как параметр. Это работает для нас.

Ответ 15

Я не вижу упоминания о json-typcript-mapper: https://www.npmjs.com/package/json-typescript-mapper. Насколько я могу судить, это выглядит как комбинация поиска @PhilipMiglinci и ответа @Pak.

Ответ 16

Я использовал эту библиотеку здесь: https://github.com/pleerock/class-transformer

<script lang="ts">
    import { plainToClass } from 'class-transformer';
</script>

Реализация:

private async getClassTypeValue() {
  const value = await plainToClass(ProductNewsItem, JSON.parse(response.data));
}

Иногда вам нужно будет проанализировать значения JSON для plainToClass, чтобы понять, что это данные в формате JSON

Ответ 17

В конце TS вы можете сделать так:

const isMyInterface = (val: any): val is MyInterface => {
  if (!val) { return false; }
  if (!val.myProp) { return false; }
  return true;
};

И чем пользователь, как это:

if (isMyInterface(data)) {
 // now data will be type of MyInterface
}

Ответ 18

Я столкнулся с аналогичной необходимостью. Я хотел что-то, что даст мне легкое преобразование из/в JSON это происходит от вызова API REST в/из определенного определения класса. Решения, которые я нашел, были недостаточными или предназначались для переписывания моих код классов и добавление аннотаций или аналогичных элементов.

Я хотел, чтобы что-то вроде GSON использовалось в Java для сериализации/десериализации классов в/из объектов JSON.

В связи с более поздней необходимостью того, что конвертер будет работать и в JS, я закончил писать свой собственный пакет.

Это имеет, хотя, немного накладных расходов. Но при запуске его очень удобно добавлять и редактировать.

Вы инициализируете модуль с помощью:

  1. схема преобразования - позволяет сопоставить поля и определить как будет выполнено преобразование
  2. Массив карты классов
  3. Карта функций преобразования - для специальных преобразований.

Затем в своем коде вы используете инициализированный модуль, например:

const convertedNewClassesArray : MyClass[] = this.converter.convert<MyClass>(jsonObjArray, 'MyClass');

const convertedNewClass : MyClass = this.converter.convertOneObject<MyClass>(jsonObj, 'MyClass');

или в JSON:

const jsonObject = this.converter.convertToJson(myClassInstance);

Используйте эту ссылку на пакет npm, а также подробное объяснение того, как работать с модулем: json-class-converter

Также обернул это для
Угловое использование в:   angular JSON класс-преобразователь

Ответ 19

Я использую Angular 6 в веб-интерфейсе и Spring Boot-приложение в бэкэнде, которое возвращает объекты Java. Все, что мне нужно сделать, это определить аналогичный класс в угловом приложении, у которого есть соответствующие свойства, и тогда я могу принять объект "как" объект углового класса (comp как Company в следующем примере).

См. Код переднего конца ниже, например. Дайте мне знать в комментариях, если что-то нуждается в большей ясности.

  createCompany() {
    let company = new Company();
    company.name = this.companyName;

    this.companyService.createCompany(company).subscribe(comp => {
       if (comp !== null) {
        this.employerAdminCompany = comp as Company;
      }
    });
  }

где company - это объект-сущность в весеннем загрузочном приложении, а также класс в Angular.

export class Company {
    public id: number;
    public name: string;
    public owner: User;
    public locations: Array<Location>;
}

Ответ 20

Это простой и действительно хороший вариант

let person = "{"name":"Sam","Age":"30"}";

const jsonParse: ((key: string, value: any) => any) | undefined = undefined;
let objectConverted = JSON.parse(textValue, jsonParse);

И тогда у вас будет

objectConverted.name

Ответ 21

Передайте объект как есть конструктору класса; Нет соглашений или проверок

interface iPerson {
   name: string;
   age: number;
}

class Person {
   constructor(private person: iPerson) { }

   toString(): string {
      return this.person.name + ' is ' + this.person.age;
   }  
}


// runs this as // 
const object1 = { name: 'Watson1', age: 64 };
const object2 = { name: 'Watson2' };            // age is missing

const person1 = new Person(object1);
const person2 = new Person(object2 as iPerson); // now matches constructor

console.log(person1.toString())  // Watson1 is 64
console.log(person2.toString())  // Watson2 is undefined