[英]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-ts
中Json
模塊的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
)並且只有JsonRecord
與Record
兼容。
如何將Json
轉換為Record
(或任何讓我“查找”鍵值的東西)? 或者也許將我的Json
對象縮小到JsonRecord
?
按照fromFoldableMap
的zipObject
示例,我嘗試執行以下操作但沒有成功:
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.parse
或J.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.