Я получаю JSON-объект от AJAX-вызова к REST-серверу. Этот объект имеет имена свойств, которые соответствуют моему классу TypeScript (это продолжение этого вопроса ).
Каков наилучший способ его инициализации? Я не думаю, что это будет работать, потому что у класса (& JSON-объект) есть члены, которые являются списками объектов, и члены, которые являются классами, и у этих классов есть члены, которые являются списками и / или классами.
Но я бы предпочел подход, который ищет имена членов и присваивает их всем, создавая списки и создавая экземпляры классов по мере необходимости, поэтому мне не нужно писать явный код для каждого члена в каждом классе (есть много!)
json
typescript
Дэвид Тилен
источник
источник
Ответы:
Вот несколько быстрых снимков, чтобы показать несколько разных способов. Они ни в коем случае не являются «полными» и, как отказ от ответственности, я не думаю, что это хорошая идея сделать это так. Кроме того, код не слишком чистый, так как я набрал его довольно быстро.
Также как примечание: Конечно, десериализуемые классы должны иметь конструкторы по умолчанию, как это имеет место во всех других языках, где я знаю о десериализации любого рода. Конечно, Javascript не будет жаловаться, если вы вызовете конструктор не по умолчанию без аргументов, но тогда класс должен быть лучше подготовлен к нему (плюс, на самом деле это не будет «типизированный способ»).
Вариант № 1: нет информации во время выполнения вообще
Проблема с этим подходом состоит главным образом в том, что имя любого члена должно соответствовать его классу. Это автоматически ограничивает вас одним членом одного типа на класс и нарушает несколько правил хорошей практики. Я настоятельно рекомендую против этого, но просто перечислите это здесь, потому что это был первый «черновик», когда я написал этот ответ (именно поэтому и имена «Foo» и т. Д.).
Вариант № 2: имя свойства
Чтобы избавиться от проблемы в варианте № 1, нам нужна некоторая информация о типе узла в объекте JSON. Проблема в том, что в Typescript эти вещи являются конструкциями времени компиляции, и они нужны нам во время выполнения, но объекты времени выполнения просто не знают о своих свойствах, пока они не установлены.
Один из способов сделать это - сделать так, чтобы классы знали их имена. Это свойство также необходимо в JSON. На самом деле, вам нужно только это в JSON:
Вариант № 3: явное указание типов элементов
Как указано выше, информация о типах членов класса недоступна во время выполнения - если только мы не сделаем ее доступной. Нам нужно сделать это только для не примитивных членов, и мы готовы пойти:
Вариант № 4: подробный, но аккуратный способ
Обновление 01/03/2016: Как отметил @GameAlchemist в комментариях ( идея , реализация ), начиная с Typescript 1.7, решение, описанное ниже, может быть написано лучше с использованием декораторов классов / свойств.
Сериализация - это всегда проблема, и, на мой взгляд, лучший способ - это путь, который не самый короткий. Из всех вариантов это то, что я бы предпочел, потому что автор класса имеет полный контроль над состоянием десериализованных объектов. Если бы мне пришлось угадывать, я бы сказал, что все другие варианты рано или поздно доставят вам неприятности (если только в Javascript не найдется собственный способ решения этой проблемы).
Действительно, следующий пример не делает гибкость справедливой. Это действительно просто копирует структуру класса. Разница, которую вы должны иметь в виду, заключается в том, что у класса есть полный контроль над использованием любого вида JSON, который он хочет контролировать с помощью состояния всего класса (вы можете вычислять вещи и т. Д.).
источник
equals
илиtoString
методов (только то, что они обычно генерируются автоматически). Не должно быть слишком сложно написать генератор,deserialize
если вы этого хотите, но это просто не может быть автоматизация во время выполнения.вы можете использовать,
Object.assign
я не знаю, когда это было добавлено, я в настоящее время использую Typescript 2.0.2, и это, кажется, функция ES6.вот
HalJson
вот что говорит хром
так что вы можете видеть, что это не делает назначение рекурсивно
источник
Object.assign
. Почему тогда у нас есть два лексиконоподобных ответа над этим?Object.assign
что не будет работать рекурсивно и не будет создавать экземпляры правильных типов объектов, оставляя значения в качествеObject
экземпляров. Хотя это подходит для тривиальных задач, сериализация сложных типов невозможна. Например, если свойство класса имеет пользовательский тип класса,JSON.parse
+Object.assign
будет создавать экземпляр этого свойства дляObject
. Побочные эффекты включают отсутствующие методы и средства доступа.Object.assign
, когда все еще сводится к написанию вложенных экземпляров с помощью рука. Этот подход хорош для очень простых объектов учебного уровня, но не для реального использования.TLDR: TypedJSON (рабочее доказательство концепции)
Корень сложности этой проблемы в том, что нам нужно десериализовать JSON во время выполнения, используя информацию о типах, которая существует только во время компиляции . Это требует, чтобы информация о типах была как-то доступна во время выполнения.
К счастью, это можно решить очень элегантным и надежным способом с помощью декораторов и ReflectDecorators :
Тип записи-информация
С помощью комбинации ReflectDecorators и декораторов свойств можно легко записать информацию о типе свойства. Элементарная реализация этого подхода будет:
Для любого данного свойства приведенный выше фрагмент добавит ссылку на функцию-конструктор свойства к скрытому
__propertyTypes__
свойству в прототипе класса. Например:И все, у нас есть требуемая информация о типе во время выполнения, которая теперь может быть обработана.
Тип обработки информации
Сначала нам нужно получить
Object
экземпляр, используяJSON.parse
- после этого мы можем перебрать все входы в__propertyTypes__
(собранные выше) и создать соответствующие свойства соответственно. Тип корневого объекта должен быть указан, чтобы десериализатор имел отправную точку.Опять же, мертвой простой реализацией этого подхода будет:
Приведенная выше идея имеет большое преимущество десериализации ожидаемыми типами (для сложных / объектных значений) вместо того, что присутствует в JSON. Если
Person
ожидается, что этоPerson
экземпляр, который создан. С некоторыми дополнительными мерами безопасности для примитивных типов и массивов этот подход можно сделать безопасным, который противостоит любому вредоносному JSON.Краевые Чехлы
Однако, если вы теперь довольны тем, что решение настолько простое, у меня есть плохие новости: существует огромное количество крайних случаев, о которых нужно позаботиться. Только некоторые из которых:
Если вы не хотите возиться со всем этим (держу пари, что нет), я был бы рад порекомендовать рабочую экспериментальную версию проверки концепции, использующей этот подход, TypedJSON - которую я создал чтобы решить именно эту проблему, проблема, с которой я сталкиваюсь каждый день.
Из-за того, что декораторы все еще считаются экспериментальными, я бы не рекомендовал использовать их для производственного использования, но до сих пор это хорошо мне помогало.
источник
Я использовал этого парня, чтобы сделать работу: https://github.com/weichx/cerialize
Это очень просто, но мощно. Поддерживает:
Пример:
источник
Я создал инструмент, который генерирует интерфейсы TypeScript и «карту типов» во время выполнения для выполнения проверки типов во время выполнения по результатам
JSON.parse
: ts.quicktype.ioНапример, учитывая этот JSON:
quicktype создает следующий интерфейс TypeScript и карту типов:
Затем мы проверяем результат
JSON.parse
по карте типа:Я пропустил некоторый код, но вы можете попробовать quicktype для деталей.
источник
Вариант № 5: Использование конструкторов Typescript и jQuery.extend
Это наиболее приемлемый метод: добавьте конструктор, который принимает в качестве параметра структуру json, и расширьте объект json. Таким образом, вы можете разобрать структуру json во всей модели приложения.
Нет необходимости создавать интерфейсы или перечислять свойства в конструкторе.
В вашем обратном вызове ajax, где вы получаете компанию для расчета зарплаты:
источник
$.extend
берутся?Object.assign
, что удаляет эту зависимость от jQuery.Для простых объектов мне нравится этот метод:
Использование возможности определения свойств в конструкторе позволяет ему быть кратким.
Это дает вам типизированный объект (против всех ответов, которые используют Object.assign или какой-либо вариант, который дает вам объект) и не требует внешних библиотек или декораторов.
источник
Описанный выше 4-й вариант - это простой и приятный способ сделать это, который должен сочетаться с 2-м вариантом в случае, когда вам нужно обрабатывать иерархию классов, как, например, список членов, который является любым из вхождений подклассов Суперкласс Участника, например, Директор расширяет Участника или Студент расширяет Участника. В этом случае вы должны указать тип подкласса в формате json.
источник
JQuery .extend делает это для вас:
источник
лучшее, что я нашел для этой цели - это класс-трансформер. github.com/typestack/class-transformer
Вот как вы это используете:
Какой-то класс:
Если вы используете декоратор @Type, будут созданы и вложенные свойства.
источник
Возможно не актуальное, но простое решение:
работать на сложные зависимости тоже !!!
источник
baz
будут иметь тип,Object
а не тип.Bar.
Это работает в этом простом случае, потому чтоBar
не имеет методов (только примитивные свойства). Если быBar
был такой методisEnabled()
, этот подход потерпел бы неудачу, так как этот метод не был бы в сериализованной строке JSON.Еще один вариант использования заводов
https://github.com/MrAntix/ts-deserialize
использовать как это
источник
Лично я предпочитаю вариант № 3 @Ingo Bürk. И я улучшил его коды для поддержки массива сложных данных и массива примитивных данных.
источник
вы можете сделать как ниже
и
источник
источник