FSharp.Data - 如何处理空JSON值?

Mic*_*cah 5 f# json f#-data

我正在尝试将FSharp.Data示例转换为我正在处理的问题的解决方案,但我只是没有走得太远.

问题

给定返回json的端点类似于:

{
  Products:[{
    Id:43,
    Name:"hi"
  },
  {
    Id:45,
    Name:"other prod"
  }
  ]
}
Run Code Online (Sandbox Code Playgroud)

如何加载数据然后只Id从实际的现有数据中获取数据?

我不明白如何"模式匹配"的可能性:

  • 它什么都不会回报
  • root.Products可能是不存在/空
  • Id可能不存在

通过Null匹配尝试

namespace Printio

open System 
open FSharp.Data
open FSharp.Data.JsonExtensions

module PrintioApi =
    type ApiProducts = JsonProvider<"https://api.print.io/api/v/1/source/widget/products?recipeId=f255af6f-9614-4fe2-aa8b-1b77b936d9d6&countryCode=US">

let getProductIds url =
    async {
        let! json = ApiProducts.AsyncLoad url
        let ids = match json with
            | null  -> [||]
            | _ -> 
                match json.Products with
                    | null -> [||]
                    | _ -> Array.map (fun (x:ApiProducts.Product)-> x.Id) json.Products

        return ids
        }
Run Code Online (Sandbox Code Playgroud)

Mar*_*ann 5

编辑:当我写这个答案时,我没有完全理解 JSON 类型提供程序的功能。事实证明,您可以使用示例 JSON 文档列表填充它,这使您能够处理可能存在或不存在数据的各种场景。这些天我经常使用它,所以我不再相信我最初写的东西。我会在这里留下原始答案,以防任何人都可以从中获得任何价值。

在页面上查看我的其他答案,以演示我今天将如何做。


虽然类型提供程序很好,但我认为尝试将像 JSON 这样没有模式和类型安全的东西当作强类型数据在概念上是错误的。我没有使用类型提供程序,而是使用HttpClientJson.NETFSharp.Interop.Dynamic来编写这样的查询:

let response = client.GetAsync("").Result
let json = response.Content.ReadAsJsonAsync().Result
let links = json?links :> obj seq
let address =
    links
    |> Seq.filter (fun l -> l?rel <> null && l?href <> null)
    |> Seq.filter (fun l -> l?rel.ToString() = rel)
    |> Seq.map (fun l -> Uri(l?href.ToString()))
    |> Seq.exactlyOne
Run Code Online (Sandbox Code Playgroud)

whereclient是 的一个实例HttpClientReadAsJsonAsync是一个像这样定义的小助手方法:

type HttpContent with
    member this.ReadAsJsonAsync() =
        let readJson (t : Task<string>) =
            JsonConvert.DeserializeObject t.Result
        this.ReadAsStringAsync().ContinueWith(fun t -> readJson t)
Run Code Online (Sandbox Code Playgroud)


Isa*_*ham 2

You probably don't need pattern matching for checking whether it's an empty array of not if you have some level of confidence in the source data. Something like this might just work fine: -

let getProductIds url =
    async {
        let! json = ApiProducts.AsyncLoad url
        return json.Products |> Seq.map(fun p -> p.Id) |> Seq.cache
    }
Run Code Online (Sandbox Code Playgroud)

Note you shouldn't use Async.RunSynchronously when in an async { } block - you can do a let! binding which will await the result asynchronously.