簡體   English   中英

如何在 Json (fp-ts) 類型中查找鍵的值?

[英]How to lookup the value for a key in type Json (fp-ts)?

我可以使用以下代碼從 Option 中的 json 字符串中提取一個值:

有效的代碼

import { identity, pipe } from "fp-ts/lib/function";
import * as E from "fp-ts/Either";
import * as O from "fp-ts/lib/Option";
import * as R from "fp-ts/lib/Record";

const jsonString = '{"a": 1}';
const a = pipe(
  jsonString,
  (x) =>
    E.tryCatch(
      () => JSON.parse(x),
      (reason) => new Error(String(reason))
    ),
  O.fromEither,
  O.chain(R.lookup("a"))
);
console.log(a);

E.tryCatch調用產生的類型是Either<Error, any>並且R.lookup似乎接受any

不起作用的代碼

我不想直接使用JSON.parse ,而是使用fp-tsJson模塊的parse

import { identity, pipe } from "fp-ts/lib/function";
import * as J from "fp-ts/lib/Json";
import * as O from "fp-ts/lib/Option";
import * as R from "fp-ts/lib/Record";
const jsonString = '{"a": 1}';
const a = pipe(
  jsonString,
  J.parse,
  O.fromEither,
  O.chain(R.lookup("a"))  <-- Error
);

嘗試在 =Json= 上使用 =R.lookup= 會出現以下錯誤:

Argument of type '<A>(r: Record<string, A>) => Option<A>' 
is not assignable to parameter of type '(a: Json) => Option<Json>'

我猜這是因為Json可以有很多值( boolean | number | string | null | JsonArray | JsonRecord )並且只有JsonRecordRecord兼容。

問題

如何將Json轉換為Record (或任何讓我“查找”鍵值的東西)? 或者也許將我的Json對象縮小到JsonRecord

我試過的

按照fromFoldableMapzipObject示例,我嘗試執行以下操作但沒有成功:

import { identity, pipe } from "fp-ts/lib/function";
import * as J from "fp-ts/lib/Json";
import * as O from "fp-ts/lib/Option";
import * as R from "fp-ts/lib/Record";
const jsonString = '{"a": 1}';
const a = pipe(
  jsonString,
  J.parse,
  O.fromEither,
  O.map((kvs) => R.fromFoldableMap(last<J.Json>(), R.Foldable)(kvs, identity)), <-- Not working
  O.chain(R.lookup("a"))
);
 

出現錯誤的原因是因為Json類型不一定可分配給Record 例如,字符串'11'將是JSON.parseJ.parse的有效輸入,並會產生number 11 該數字不能像Record一樣使用。 它也可以是數組、字符串或 null 等。您需要進一步解析返回的值,然后才能將其與R.lookup一起使用。

為了解決這個問題,我能想到的最直接的方法是將io-ts * 添加到組合中。

我懷疑最簡單的是:

import { pipe } from "fp-ts/lib/function";
import * as J from "fp-ts/lib/Json";
import * as E from "fp-ts/lib/Either";
import * as O from "fp-ts/lib/Option";
import * as R from "fp-ts/lib/Record";
import * as t from "io-ts";

const jsonString = '{"a": 1}';
const a = pipe(
  jsonString,
  J.parse,
  E.chainW(t.UnknownRecord.decode), // Different Error type so chainW
  O.fromEither,
  O.chain(R.lookup("a"))
);

如果您要利用io-ts並且您知道要解析的對象的大致形狀,那么您可以通過以下方式改進這種方法:

import { pipe } from "fp-ts/lib/function";
import * as J from "fp-ts/lib/Json";
import * as E from "fp-ts/lib/Either";
import * as O from "fp-ts/lib/Option";
import * as t from "io-ts";

const jsonString = '{"a": 1}';
// Here you can define the exact shape of what you
// think J.parse will have returned.
const schema = t.type({
  a: t.number
});
const a = pipe(
  jsonString,
  J.parse,
  // Decode will then match that shape or return a `E.left` if it fails to.
  E.chainW(schema.decode),
  O.fromEither,
  // The type of innerA is number and no need to do `lookup`
  // because the entire shape of the object has been validated
  O.map(({ a: innerA }) => innerA)
);

為了絕對完整性,僅使用fp-ts我認為您可以獲得如下工作:

import { pipe } from "fp-ts/lib/function";
import * as J from "fp-ts/lib/Json";
import * as R from "fp-ts/lib/Record";
import * as O from "fp-ts/lib/Option";

const jsonString = '{"a": 1}';
// Note that the return type here is going to be `Option<Json>` which
// may still be difficult to work with in your code for the same reason
// that you couldn't use `R.lookup` directly. Parsing with `io-ts` will
// give you easier types to work with.
const a = pipe(
  jsonString,
  J.parse,
  O.fromEither,
  O.chain((json) => {
    // This is basically just inlining what `io-ts` would do for you so
    // I again would recommend that library.
    if (typeof json === "object" && json !== null && !(json instanceof Array)) {
      return pipe(json, R.lookup("a"));
    } else {
      return O.none;
    }
  })
);

我要提到的最后一件事是,如果你真的希望這個函數能夠以盡可能少的變化工作,並且你可以使用O.Option<J.Json>返回類型,並且你可以斷言字符串將始終是一個 JSON 對象,並且您真的不想使用io-ts ,您可以通過如下類型斷言來實現您想要的:

import { pipe } from "fp-ts/lib/function";
import * as J from "fp-ts/lib/Json";
import * as O from "fp-ts/lib/Option";
import * as R from "fp-ts/lib/Record";
const jsonString = '{"a": 1}';
const a = pipe(
  jsonString,
  J.parse,
  O.fromEither,
  // Again this is unsafe because the value might be an Array or a primitive
  // value. You're basically just escaping from the type system.
  O.map((json: J.Json): J.JsonRecord => json as J.JsonRecord),
  O.chain(R.lookup("a"))
);

但我不推薦這種方法,因為我認為使用返回值會很困難,並且如果輸入的 json 字符串不是對象,那么你就會面臨錯誤。


* io-ts是來自制作fp-ts的同一人的庫,用於驗證 TypeScript 中unknown數據的形狀。

當你得到它並在你的程序中使用被解析的對象時,你可以直接解析它

const jsonString = '{"a": 1}';
var parsedJsonString = JSON.parse(jsonString);

console.log( "Parsed JSON: ", parsedJsonString );
console.log( "a:", parsedJsonString.a );

為了更安全一點,您可以檢查它是否真的是這樣的 json 字符串:

function isJsonString(jsonString){
    try {
        JSON.parse(jsonString);
    } catch (e) {
        return false;
    }
    return true;
}

const jsonString = '{"a": 1}';
var isValidJson = isJsonString( jsonString );
if( isValidJson ){
    var parsedJsonString = JSON.parse(jsonString);

    console.log( "Parsed JSON: ", parsedJsonString );
    console.log( "a:", parsedJsonString.a );
}else{
    console.log("jsonString is not a valid json");
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM