如何解析Typescript中的JSON字符串


99

有没有一种方法可以将Typescript中的字符串解析为JSON。
示例:在JS中,我们可以使用JSON.parse()。Typescript中有类似的功能吗?

我有一个JSON对象字符串,如下所示:

{"name": "Bob", "error": false}

1
在其首页上显示“ TypeScript是JavaScript的类型化超集,可编译为纯JavaScript”。JSON.parse()函数应该像通常一样可用。
sigalor '16

1
我正在使用Atom文本编辑器,当我执行JSON.parse时,出现错误:类型'{}'的参数无法分配给类型'string'的参数
ssd20072 '16

21
这是一个非常基本的问题,对于某些人来说似乎微不足道,但仍然是一个有效的问题,因此在SO中找不到等效项(我没有),因此没有真正的理由不保留该问题运行,我认为也不应该被否决。
Nitzan Tomer

2
@SanketDeshpande使用时,您将JSON.parse得到一个对象而不是对象string(有关更多信息,请参阅我的答案)。如果要将对象转换为字符串,则需要使用JSON.stringify
Nitzan Tomer'7

2
实际上,由于两个原因,这不是一个简单的问题。首先,JSON.parse()不返回相同类型的对象-它会匹配某些接口,但是不会显示任何智能对象(例如访问器)。此外,我们当然希望SO成为人们在使用Google产品时的去向吗?
物种未知

Answers:


165

Typescript是javascript(的超集),因此您可以JSON.parse像在javascript中那样使用:

let obj = JSON.parse(jsonString);

只有在打字稿中,您才能对结果对象进行打字:

interface MyObj {
    myString: string;
    myNumber: number;
}

let obj: MyObj = JSON.parse('{ "myString": "string", "myNumber": 4 }');
console.log(obj.myString);
console.log(obj.myNumber);

操场上的代码


9
如何验证输入是否有效(类型检查,打字稿的目的之一)?更换输入'{ "myString": "string", "myNumber": 4 }''{ "myString": "string", "myNumberBAD": 4 }'不会失败,并obj.myNumber将返回undefined。
David Portabella

2
@DavidPortabella您不能对字符串的内容进行类型检查。这是一个运行时问题,类型检查用于编译时
Nitzan Tomer

2
好。如何在运行时验证打字稿obj是否满足其接口?也就是说,在此示例中未定义myNumber。例如,在Scala Play中,您将使用Json.parse(text).validate[MyObj]playframework.com/documentation/2.6.x/ScalaJson您如何在打字稿中做同样的事情(也许有一个外部库这样做?)?
David Portabella

1
@DavidPortabella无法做到这一点,并不容易,因为在运行时MyObj不存在。SO中还有许多与此主题相关的其他线程,例如:检查对象是否在运行时使用TypeScript实现接口
Nitzan Tomer

7
好,谢谢。每天,我对使用scalaj更有信心。
David Portabella

5

类型安全 JSON.parse

您可以继续使用JSON.parse,因为TS是JS超集。仍然存在一个问题:JSON.parsereturn any,这会破坏类型安全性。对于更强的类型,有两种选择:

1.用户自定义类型的警卫(游乐场

自定义类型防护是最简单的解决方案,通常足以用于外部数据验证:

// For example, you expect to parse a given value with `MyType` shape
type MyType = { name: string; description: string; }

// Validate this value with a custom type guard
function isMyType(o: any): o is MyType {
  return "name" in o && "description" in o
}

然后,JSON.parse包装器可以将类型防护作为输入并返回已解析的键入值:

const safeJsonParse = <T>(guard: (o: any) => o is T) => (text: string): ParseResult<T> => {
  const parsed = JSON.parse(text)
  return guard(parsed) ? { parsed, hasError: false } : { hasError: true }
}

type ParseResult<T> =
  | { parsed: T; hasError: false; error?: undefined }
  | { parsed?: undefined; hasError: true; error?: unknown }
用法示例:
const json = '{ "name": "Foo", "description": "Bar" }';
const result = safeJsonParse(isMyType)(json) // result: ParseResult<MyType>
if (result.hasError) {
  console.log("error :/")  // further error handling here
} else {
  console.log(result.parsed.description) // result.parsed now has type `MyType`
}

safeJsonParse可能会扩展为快速失败或尝试/捕获JSON.parse错误。

2.外部库

如果需要验证许多不同的值,则手动编写类型保护功能会变得很麻烦。有一些库可以帮助完成此任务-示例(无完整列表):

更多信息


4

如果希望JSON具有经过验证的Typescript类型,则需要自己进行验证。这不是什么新鲜事。在纯Javascript中,您需要执行相同的操作。

验证方式

我喜欢将验证逻辑表示为一组“变换”。我将a定义Descriptor为变换图:

type Descriptor<T> = {
  [P in keyof T]: (v: any) => T[P];
};

然后,我可以创建一个将这些转换应用于任意输入的函数:

function pick<T>(v: any, d: Descriptor<T>): T {
  const ret: any = {};
  for (let key in d) {
    try {
      const val = d[key](v[key]);
      if (typeof val !== "undefined") {
        ret[key] = val;
      }
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      throw new Error(`could not pick ${key}: ${msg}`);
    }
  }
  return ret;
}

现在,我不仅要验证我的JSON输入,而且还要在构建Typescript类型。上面的泛型类型可确保结果从“转换”中推断出类型。

万一转换抛出一个错误(这是实现验证的方式),我想用另一个错误包装它,显示哪个键导致了错误。

用法

在您的示例中,我将按以下方式使用它:

const value = pick(JSON.parse('{"name": "Bob", "error": false}'), {
  name: String,
  error: Boolean,
});

现在value将被键入,因为StringBoolean都是“变形金刚”,它们在某种意义上是接受输入并返回键入的输出。

此外,value实际上是该类型。换句话说,如果name实际上是123,它将被转换为,"123"以便您具有有效的字符串。这是因为我们String在运行时使用了一个内置函数,该函数接受任意输入并返回a string

您可以在这里看到此工作。尝试以下方法来说服自己:

  • 将鼠标悬停在const value定义上可以看到弹出窗口显示正确的类型。
  • 尝试更改"Bob"123并重新运行示例。在控制台中,您将看到名称已正确转换为string "123"

你举了一个例子,“如果name实际上123,它将会转化成"123"这似乎是不正确的我。value是回来了{name: 123..没有{name:"123"..,当我复制粘贴正是所有的代码,并做出的改变。
Joncom

很奇怪,对我有用。在此处尝试:typescriptlang.org/play/index.html(使用123代替"Bob")。
chowey

我认为您不需要定义Transformed类型。您可以使用Objecttype Descriptor<T extends Object> = { ... };
lovasoa

谢谢@lovasoa,您是正确的。该Transformed类型完全没有必要。我已经相应地更新了答案。
chowey

1

您还可以使用执行json类型验证的库,例如Sparkson。它们允许您定义TypeScript类,您希望将其解析为响应,在这种情况下,它可以是:

import { Field } from "sparkson";
class Response {
   constructor(
      @Field("name") public name: string,
      @Field("error") public error: boolean
   ) {}
}

该库将验证JSON负载中是否存在必填字段以及它们的类型是否正确。它还可以进行大量的验证和转换。


您应该提到,您是上述库的主要贡献者。
ford04

1

ts-json-object有一个很棒的库

在您的情况下,您需要运行以下代码:

import {JSONObject, required} from 'ts-json-object'

class Response extends JSONObject {
    @required
    name: string;

    @required
    error: boolean;
}

let resp = new Response({"name": "Bob", "error": false});

该库将在解析前验证json


0

JSON.parse 在TypeScript中可用,因此您可以使用它:

JSON.parse('{"name": "Bob", "error": false}') // Returns a value of type 'any'

但是,您经常需要解析JSON对象,同时确保它匹配某个特定类型,而不是处理type的值any。在这种情况下,您可以定义以下函数:

function parse_json<TargetType extends Object>(
  json: string,
  type_definitions: { [Key in keyof TargetType]: (raw_value: any) => TargetType[Key] }
): TargetType {
  const raw = JSON.parse(json); 
  const result: any = {};
  for (const key in type_definitions) result[key] = type_definitions[key](raw[key]);
  return result;
}

该函数接受一个JSON字符串和一个对象,该对象包含加载要创建的对象的每个字段的各个函数。您可以这样使用它:

const value = parse_json(
  '{"name": "Bob", "error": false}',
  { name: String, error: Boolean, }
);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.