Cara mengurai string JSON di Typecript

110

Apakah ada cara untuk mengurai string sebagai JSON di Typecript.
Contoh: Di JS, kita bisa menggunakan JSON.parse(). Apakah ada fungsi serupa di Ketikan?

Saya memiliki string objek JSON sebagai berikut:

{"name": "Bob", "error": false}
ssd20072.dll
sumber
1
Di beranda, dikatakan bahwa "TypeScript adalah superset dari JavaScript yang dikompilasi ke JavaScript biasa". Fungsi JSON.parse () harus bisa digunakan seperti biasa.
sigalor
1
Saya menggunakan editor teks Atom dan ketika saya melakukan JSON.parse, saya mendapatkan kesalahan: Argumen tipe '{}' tidak dapat ditetapkan ke parameter tipe 'string'
ssd20072
23
Ini adalah pertanyaan yang sangat mendasar, dan mungkin tampak sepele bagi beberapa orang tetapi ini adalah pertanyaan yang valid, dan padanan tidak dapat ditemukan di SO (saya belum) jadi tidak ada alasan nyata mengapa tidak menyimpan pertanyaan berjalan, dan menurut saya tidak boleh turun juga.
Nitzan Tomer
2
@SanketDeshpande Ketika Anda menggunakan JSON.parseAnda mendapatkan objek sebagai hasil dan bukan string(lihat jawaban saya untuk lebih lanjut). Jika Anda ingin mengubah objek menjadi string maka Anda perlu menggunakan JSON.stringifysebagai gantinya.
Nitzan Tomer
2
Sebenarnya ini bukan pertanyaan sederhana karena 2 alasan. Pertama, JSON.parse () tidak mengembalikan jenis objek yang sama - ini akan cocok dengan beberapa antarmuka tetapi apapun yang cerdas, seperti pengakses, tidak akan ada. Selain itu, tentunya kita ingin SO berada di tempat yang dikunjungi orang saat mereka menggunakan Google?
spesiesTidak Diketahui

Jawaban:

183

Ketikan adalah (superset dari) javascript, jadi Anda hanya menggunakan JSON.parseseperti yang Anda lakukan di javascript:

let obj = JSON.parse(jsonString);

Hanya di skrip ketikan Anda bisa memiliki tipe ke objek yang dihasilkan:

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

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

( kode di taman bermain )

Nitzan Tomer
sumber
10
bagaimana memvalidasi bahwa input tersebut valid (pengecekan tipe, salah satu tujuan skrip ketikan)? mengganti input '{ "myString": "string", "myNumber": 4 }'dengan '{ "myString": "string", "myNumberBAD": 4 }'tidak akan gagal, dan obj.myNumber akan mengembalikan undefined.
David Portabella
3
@DavidPortabella Anda tidak dapat melakukan pemeriksaan tipe pada konten string. Ini adalah masalah runtime, dan pemeriksaan jenis untuk waktu kompilasi
Nitzan Tomer
2
baik. bagaimana saya dapat memvalidasi bahwa object script memenuhi antarmuka pada saat runtime? artinya, myNumber tidak ditentukan dalam contoh ini. misalnya, di Scala Play, Anda akan menggunakan Json.parse(text).validate[MyObj]. playframework.com/documentation/2.6.x/ScalaJson bagaimana Anda bisa melakukan hal yang sama dalam skrip ketikan (mungkin ada perpustakaan eksternal untuk melakukannya?)?
David Portabella
1
@DavidPortabella Tidak ada cara untuk melakukan itu, tidak mudah, karena saat runtime MyObjtidak ada. Ada banyak utas lain di SO tentang subjek ini, misalnya: Periksa apakah suatu objek mengimplementasikan antarmuka saat runtime dengan TypeScript
Nitzan Tomer
7
Ok terima kasih. setiap hari saya lebih yakin tentang menggunakan scalajs.
David Portabella
8

Ketik aman JSON.parse

Anda dapat terus menggunakan JSON.parse, karena TS adalah superset JS. Masih ada masalah tersisa: JSON.parsepengembalian any, yang merusak keamanan tipe. Berikut dua opsi untuk tipe yang lebih kuat:

1. Pelindung tipe yang ditentukan pengguna ( taman bermain )

Pelindung tipe kustom adalah solusi paling sederhana dan seringkali cukup untuk validasi data eksternal:

// 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
}

Sebuah JSON.parsewrapper kemudian dapat mengambil penjaga tipe sebagai masukan dan kembali diurai, nilai diketik:

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 }
Contoh penggunaan:
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`
}

safeJsonParsemungkin diperpanjang untuk gagal dengan cepat atau mencoba / menangkap JSON.parsekesalahan.

2. Perpustakaan eksternal

Menulis fungsi pelindung tipe secara manual menjadi rumit, jika Anda perlu memvalidasi banyak nilai yang berbeda. Ada perpustakaan untuk membantu tugas ini - contoh (tidak ada daftar lengkap):

Info selengkapnya

ford04
sumber
4

Jika Anda ingin JSON Anda memiliki tipe Typecript yang divalidasi, Anda perlu melakukan pekerjaan validasi itu sendiri. Ini bukanlah hal baru. Dalam Javascript biasa, Anda perlu melakukan hal yang sama.

Validasi

Saya suka mengekspresikan logika validasi saya sebagai satu set "transformasi". Saya mendefinisikan a Descriptorsebagai peta transformasi:

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

Kemudian saya dapat membuat fungsi yang akan menerapkan transformasi ini ke input arbitrer:

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

Sekarang, saya tidak hanya memvalidasi input JSON saya, tetapi saya juga membangun tipe Typecript saat saya pergi. Jenis umum di atas memastikan bahwa hasil menyimpulkan jenis dari "transformasi" Anda.

Jika transformasi memunculkan kesalahan (yang merupakan cara Anda menerapkan validasi), saya ingin membungkusnya dengan kesalahan lain yang menunjukkan kunci mana yang menyebabkan kesalahan.

Pemakaian

Dalam contoh Anda, saya akan menggunakan ini sebagai berikut:

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

Sekarang valueakan diketik, karena Stringdan Booleankeduanya adalah "transformer" dalam arti mereka mengambil input dan mengembalikan output yang diketik.

Lebih jauh lagi, sebenarnyavalue akan menjadi tipe itu. Dengan kata lain, jika namesebenarnya 123, itu akan diubah "123"sehingga Anda memiliki string yang valid. Ini karena kami menggunakan Stringpada waktu proses, fungsi bawaan yang menerima input arbitrer dan mengembalikan file string.

Anda dapat melihat ini berfungsi di sini . Cobalah hal-hal berikut untuk meyakinkan diri sendiri:

  • Arahkan kursor ke const valuedefinisi untuk melihat bahwa pop-over menampilkan jenis yang benar.
  • Cobalah mengubah "Bob"ke 123dan kembali menjalankan sampel. Di konsol Anda, Anda akan melihat bahwa nama tersebut telah diubah dengan benar menjadi string "123".
chowey
sumber
Anda memberi contoh, "jika namebenar-benar 123, itu akan diubah menjadi "123". Ini sepertinya tidak benar. Saya valueakan kembali {name: 123..bukan {name:"123"..ketika saya menyalin dan menempel semua kode Anda dengan tepat dan membuat perubahan itu.
Joncom
Aneh, itu berhasil untuk saya. Coba di sini: typescriptlang.org/play/index.html (menggunakan 123alih-alih "Bob").
chowey
Saya tidak berpikir Anda perlu mendefinisikan Transformedtipe. Anda bisa menggunakan Object. type Descriptor<T extends Object> = { ... };
lovasoa
Terima kasih @lovasoa, Anda benar. The Transformedjenis adalah sama sekali tidak perlu. Saya telah memperbarui jawabannya.
chowey
Jika Anda benar-benar ingin memvalidasi bahwa Objek JSON memiliki tipe yang benar, Anda tidak ingin 123secara otomatis diubah menjadi string "123", karena itu adalah angka dalam objek JSON.
xuiqzy
1

Anda juga dapat menggunakan pustaka yang melakukan validasi tipe json Anda, seperti Sparkson . Mereka memungkinkan Anda untuk menentukan kelas TypeScript, yang ingin Anda parse responsnya, dalam kasus Anda bisa jadi:

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

Pustaka akan memvalidasi jika bidang wajib ada dalam payload JSON dan jika jenisnya benar. Itu juga dapat melakukan banyak validasi dan konversi.

jfu
sumber
1
Anda harus menyebutkan, bahwa Anda adalah kontributor utama perpustakaan di atas.
ford04
1

Ada perpustakaan yang bagus untuk itu ts-json-object

Dalam kasus Anda, Anda perlu menjalankan kode berikut:

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

Library ini akan memvalidasi json sebelum melakukan parsing

Elisha Sterngold
sumber
0

JSON.parse tersedia dalam TypeScript, jadi Anda bisa menggunakannya:

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

Namun, Anda sering kali ingin mengurai objek JSON sambil memastikannya cocok dengan tipe tertentu, daripada berurusan dengan nilai tipe any. Dalam hal ini, Anda dapat menentukan fungsi seperti berikut:

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

Fungsi ini mengambil string JSON dan objek yang berisi fungsi individual yang memuat setiap bidang objek yang Anda buat. Anda dapat menggunakannya seperti ini:

const value = parse_json(
  '{"name": "Bob", "error": false}',
  { name: String, error: Boolean, }
);
lovasoa
sumber