TypeScript'te JSON dizesi nasıl ayrıştırılır


111

Dizeleri Typecript'te JSON olarak ayrıştırmanın bir yolu var mı?
Örnek: JS'de kullanabiliriz JSON.parse(). Typescript'te benzer bir işlev var mı?

Aşağıdaki gibi bir JSON nesne dizim var:

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

1
Ana sayfasında "TypeScript, düz JavaScript’i derleyen JavaScript’in yazılı bir üst kümesidir" diyor. JSON.parse () işlevi normal gibi kullanılabilir olmalıdır.
sigalor

1
Atom metin düzenleyicisini kullanıyorum ve bir JSON.parse yaptığımda şu hatayı alıyorum: '{}' türündeki bağımsız değişken, 'dizge' türündeki bir parametreye atanamaz
ssd20072

23
Bu çok basit bir sorudur ve bazılarına önemsiz görünebilir, ancak hiç de az olmayan geçerli bir sorudur ve SO'da bir eşdeğer bulunamaz (ben yok), bu yüzden soruyu tutmamak için gerçek bir neden yok koşuyor ve bence de aşağı oylanmamalı.
Nitzan Tomer

2
@SanketDeshpande Kullandığınızda JSON.parsesonuç olarak bir nesne alırsınız, a değil string(daha fazlası için cevabıma bakın). Bir nesneyi dizeye dönüştürmek istiyorsanız, JSON.stringifybunun yerine kullanmanız gerekir .
Nitzan Tomer

2
Aslında 2 nedenden dolayı basit bir soru değil. İlk olarak, JSON.parse () aynı türden bir nesne döndürmez - arayüzün bir kısmıyla eşleşir, ancak erişimciler gibi akıllı herhangi bir şey mevcut olmayacaktır. Dahası, insanların google'da bir şeyler yaptıklarında gittikleri yerde SO olmasını isteriz?
türlerBilinmeyen

Yanıtlar:


184

Typescript (bir üst kümesi) javascript, yani javascript'te yaptığınız JSON.parsegibi kullanın :

let obj = JSON.parse(jsonString);

Sadece typcript'te ortaya çıkan nesnenin bir türüne sahip olabilirsiniz:

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

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

( oyun alanında kod )


10
girdinin geçerli olduğu nasıl doğrulanır (tür denetimi, typecript amaçlarından biri)? girişini değiştirerek '{ "myString": "string", "myNumber": 4 }'tarafından '{ "myString": "string", "myNumberBAD": 4 }'başarısız olmayacak ve obj.myNumber tanımsız dönecektir.
David Portabella

3
@DavidPortabella Bir dizinin içeriği üzerinde yazım denetimi yapamazsınız. Bu bir çalışma zamanı sorunu ve tür denetimi derleme zamanı içindir
Nitzan Tomer

2
tamam. bir typcript nesnesinin çalışma zamanında arayüzünü karşıladığını nasıl doğrulayabilirim? yani, bu örnekte myNumber tanımsız değildir. örneğin, Scala Play'de kullanırsınız Json.parse(text).validate[MyObj]. playframework.com/documentation/2.6.x/ScalaJson aynısını typcript'te nasıl yapabilirsiniz (bunu yapmak için harici bir kütüphane olabilir?)?
David Portabella

1
@DavidPortabella Bunu yapmanın bir yolu yok, kolay değil çünkü çalışma zamanında MyObjmevcut değil. SO'da
Nitzan Tomer

7
tamam teşekkürler. her gün scalaj kullanma konusunda daha ikna oluyorum.
David Portabella

8

Tip güvenli JSON.parse

JSON.parseTS bir JS üst kümesi olduğu için kullanmaya devam edebilirsiniz . Hala bir sorun var: tür güvenliğini baltalayan JSON.parsedönüşler any. Daha güçlü türler için iki seçenek şunlardır:

1. Kullanıcı tanımlı tip korumalar ( oyun alanı )

Özel tip korumalar en basit çözümdür ve genellikle harici veri doğrulama için yeterlidir:

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

Daha JSON.parsesonra bir sarmalayıcı, girdi olarak bir tür koruması alabilir ve ayrıştırılmış, yazılan değeri döndürebilir:

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 }
Kullanım örneği:
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`
}

safeJsonParsehızlı başarısız olmak veya dene / yakala JSON.parsehataları için uzatılabilir .

2. Dış kitaplıklar

Birçok farklı değeri doğrulamanız gerekiyorsa, yazı koruma işlevlerini manuel olarak yazmak zahmetli hale gelir. Bu göreve yardımcı olacak kitaplıklar var - örnekler (kapsamlı liste yok):

Daha fazla bilgi


4

JSON'nizin doğrulanmış bir Typescript türüne sahip olmasını istiyorsanız, bu doğrulama çalışmasını kendiniz yapmanız gerekecektir. Bu yeni bir şey değil. Düz Javascript'te aynısını yapmanız gerekir.

Doğrulama

Doğrulama mantığımı bir dizi "dönüşüm" olarak ifade etmeyi seviyorum. Bir Descriptordönüşüm haritası olarak tanımlıyorum :

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

Sonra bu dönüşümleri rastgele girdilere uygulayacak bir fonksiyon yapabilirim:

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

Şimdi, yalnızca JSON girdimi doğrulamakla kalmıyorum, aynı zamanda ilerledikçe bir Typecript türü oluşturuyorum. Yukarıdaki genel türler, sonucun "dönüşümlerinizden" türlere ulaşmasını sağlar.

Dönüşümün bir hata atması durumunda (bu, doğrulama işlemini nasıl gerçekleştirirsiniz), onu hangi anahtarın hataya neden olduğunu gösteren başka bir hatayla sarmayı tercih ederim.

Kullanım

Örneğinizde bunu şu şekilde kullanırım:

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

Şimdi valuebu yana, daktilo edilecek Stringve Booleanbunlar girişi çekmek ve daktilo çıktı dönmek anlamda hem "transformatörleri" dir.

Ayrıca, valueirade aslında olması o türü. Başka bir deyişle, eğer namegerçekten öyleyse, geçerli bir dizeye sahip olmanız 123için dönüştürülecektir "123". Bunun nedeni String, rasgele girdiyi kabul eden ve a döndüren yerleşik bir işlev olan çalışma zamanında kullanmış olmamızdır string.

Bunun çalıştığını burada görebilirsiniz . Kendinizi ikna etmek için aşağıdaki şeyleri deneyin:

  • const valueAçılır pencerenin doğru türü gösterdiğini görmek için imleci tanımın üzerine getirin .
  • Değiştirmeyi deneyin "Bob"için 123ve örnek yeniden çalıştırın. Konsolunuzda, ismin düzgün bir şekilde dizeye dönüştürüldüğünü göreceksiniz "123".

Eğer varsa," bir örnek verdi nameaslında edildi 123, bu dönüştürülecektir "123"Bu yanlış gibi görünüyor Benim.. valuegeri geliyor {name: 123..değil {name:"123"..tam olarak tüm kodu yapıştırın ve o değişikliği kopyalarken.
Joncom

Tuhaf, benim için çalışıyor. Burada deneyin: typescriptlang.org/play/index.html ( 123yerine kullanarak "Bob").
chowey

Bir Transformedtür tanımlamana gerek olduğunu sanmıyorum . Sadece kullanabilirsin Object. type Descriptor<T extends Object> = { ... };
lovasoa

Teşekkürler @lovasoa, haklısın. TransformedTipi tamamen gereksiz. Cevabı buna göre güncelledim.
chowey

JSON Nesne doğru türde olduğunu aslında validate etmek isterseniz, olur değil istediğiniz 123otomatik bir dizeye dönüştürülecek "123"o JSON nesnesi bir sayı olduğundan,.
xuiqzy

1

Ek olarak, Sparkson gibi json'unuzun tür doğrulamasını gerçekleştiren kitaplıkları kullanabilirsiniz . Yanıtınızı ayrıştırmak istediğiniz bir TypeScript sınıfı tanımlamanıza izin verir, sizin durumunuzda şunlar olabilir:

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

Kitaplık, gerekli alanların JSON yükünde mevcut olup olmadığını ve türlerinin doğru olup olmadığını doğrular. Ayrıca bir dizi doğrulama ve dönüştürme de yapabilir.


1
Yukarıdaki kütüphanenin ana katkısı olduğunuzu belirtmelisiniz.
ford04

1

Bunun için harika bir kütüphane var ts-json-object

Sizin durumunuzda aşağıdaki kodu çalıştırmanız gerekir:

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

Bu kütüphane, ayrıştırmadan önce json'u doğrular


0

JSON.parse TypeScript'te mevcuttur, yani sadece kullanabilirsiniz:

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

Ancak, genellikle bir tür değeri ile uğraşmak yerine, belirli bir türle eşleştiğinden emin olurken bir JSON nesnesini ayrıştırmak isteyeceksiniz any. Bu durumda, aşağıdaki gibi bir işlev tanımlayabilirsiniz:

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

Bu işlev bir JSON dizesi ve oluşturduğunuz nesnenin her alanını yükleyen ayrı işlevleri içeren bir nesne alır. Bunu şu şekilde kullanabilirsiniz:

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

0

TS'nin bir JavaScript çalışma zamanı var

Typescript, JS'ye derlendiğinden bir JavaScript çalışma zamanına sahiptir. Gibi dilin bir parçası olarak inşa edilmiştir Bu araçlar JS nesneler JSON, Objectve MathTS de mevcuttur. Bu nedenle JSON.parse, JSON dizesini ayrıştırmak için sadece yöntemi kullanabiliriz .

Misal:

const JSONStr = '{"name": "Bob", "error": false}'

// The JSON object is part of the runtime
const parsedObj = JSON.parse(JSONStr);

console.log(parsedObj);
// [LOG]: {
//   "name": "Bob",
//   "error": false
// } 

// The Object object is also part of the runtime so we can use it in TS
const objKeys = Object.keys(parsedObj);

console.log(objKeys);
// [LOG]: ["name", "error"] 

Şimdi tek şey, parsedObj'nin anygenellikle TS'de kötü bir uygulama olan tür olmasıdır. Yazı korumaları kullanıyorsak nesneyi yazabiliriz. İşte bir örnek:

const JSONStr = '{"name": "Bob", "error": false}'
const parsedObj = JSON.parse(JSONStr);

interface nameErr {
  name: string;
  error: boolean;
}

function isNameErr(arg: any): arg is nameErr {
  if (typeof arg.name === 'string' && typeof arg.error === 'boolean') {
    return true;
  } else {
    return false;
  }
}

if (isNameErr(parsedObj)) {
  // Within this if statement parsedObj is type nameErr;
  parsedObj
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.