我如何在F#中制作一个for循环来扫描JSON。

问题描述 投票:0回答:1

使用F#我试图扫描一个JSON文件,并将其数组与一个(随机生成的)数字数组进行比较。json的格式是:

{"1":[#,#,#,#,#],"2":[#,#,#,#,#],...}

等121个条目。 我目前正在尝试使用Json.NET。 我的问题是

  • 我如何用Json.NET导入一个本地文件?

  • 我如何设置对json key进行简单的调用,返回适合在for循环中运行的数组值?

这是我的代码,我已经做到了这一点。

open System
open System.IO
open Newtonsoft.Json

(*open FSharp.Data

type Provider = JsonProvider<"powernum.json">
let numbers = Provider.Load("powernum.json")

//numbers.``1`` gets me the array but can't scan through all the IDs with an iterating for loop
(*
if numbers.``3`` = [|29;30;41;48;64|] then
    printfn "True"
else printfn "False"
*)
(*numbers.JsonValue.Item "1" 
let test (a: int) = string a
let testPile = 
    for i = 1 to 10 do
    let goNum = numbers.JsonValue.Item (test i) 
    Console.Write goNum
    Console.WriteLine ""
testPile    // This works but is not usable for data comparison with an F# Array
*)
*)

let r = new StreamReader("\PATH\powernum.json")
let (json: string) = r.ReadToEnd();
let conv = JsonConvert.DeserializeObject<> (json)

Console.WriteLine("{0}",conv)//where I'm stuck with Json.NET

[<EntryPoint>]
let main argv =
    let rnd = Random()

    let numberGen = Set.empty.Add(rnd.Next(1,69)).Add(rnd.Next(1,69)).Add(rnd.Next(1,69)).Add(rnd.Next(1,69)).Add(rnd.Next(1,69)) |>Set.toArray |>Array.sort


    Console.ReadKey() |> ignore 
    0// return an integer exit code

Jsontocsharp.com无效。

我试过使用F# Data,但根据我的发现,不可能做一个迭代循环,因为我必须把 "key "用重音封装的数字拉起来,像这样把它读成一个int。numbers.``1``.它不接受变量。在仍然使用F# Data的情况下,尝试了另一种方法,但它只能作为一个字符串工作,当我试图转换它时,它会出错。

为了比较,这是我在python中原型的版本。

import random
import json
with open('/PATH/powernum.json') as pnumbers:
    data = json.load(pnumbers)

#makes an array with the range of numbers
Valid_numbers =[]

for x in range(69):
    Valid_numbers.append(x+1)
generated = []

def pulledNumber():
    generated[:]=[]
    #adds numbers to the array from 0-4
    while len(generated) !=5:
        #takes a random number from the range of numbers
        generate_number = random.choice(Valid_numbers)
        #check if the two arrays have the same values
        if generate_number not in generated:
            #add to the array if values don't match
            generated.append(generate_number)
    generated.sort()
    return generated

pulledNumber()

for x, y in data.items():
    if generated not in y:
        print("Id: %s passed" % x)
    else:
        print("Id: %s failed" % x)
        pulledNumber()
        break

print (pulledNumber())
json f# json.net f#-data
1个回答
4
投票

f#是一种静态类型的语言--由于它出色的类型推断,我们只是经常没有注意到。 但是当从JSON文件中反序列化时,在编写任何代码之前,确定JSON是否有固定的模式是很有用的,如果有的话,创建或选择一个合适的数据模型,JSON可以自动映射到这个模型。

在你的案例中,你的JSON看起来是这样的。

{
  "1": [29,30,41,48,64],
  "2": [29,320,441,548,11]
  // Additional items omitted
}

当你在这里有一个根对象 与变量属性名 其值总是一个整数组。 正如Newtonsoft文档中所解释的那样 反序列化一个字典这样的JSON对象可以被反序列化成一些 IDictionary<string, T> 适当的值类型 T. 而且正如在下列文件中所解释的那样: ..................................................................................................................................................................................................................................... 反序列化一个集合 JSON数组可以被反序列化为一个适当的项目类型的集合。

在f#中,我们使用 Map<'Key,'Value> 来代表一本字典,而 列表 来表示静态类型值的具体化列表。 因此,你的JSON对应的是一个

Map<string, int list>

在确定了一个合适的数据模型后,引入下面的函数,从文件中反序列化JSON。

//https://www.newtonsoft.com/json/help/html/DeserializeWithJsonSerializerFromFile.htm
let jsonFromFile<'T>(fileName : string, settings : JsonSerializerSettings) = 
    use streamReader = new StreamReader(fileName)
    use jsonReader = new JsonTextReader(streamReader)
    let serializer = JsonSerializer.CreateDefault(settings)
    serializer.Deserialize<'T>(jsonReader)

现在你可以反序列化你的JSON,并过滤符合某些列表的值。

let fileName = "\PATH\powernum.json"

let requiredValues = [29;30;41;48;64] // Or whatever list of values you are looking for

let map = jsonFromFile<Map<string, int list>>(fileName, null)

let filteredMap = 
    map |> Map.toSeq
        |> Seq.filter (fun (key, value) -> requiredValues = value)
        |> Map.ofSeq

// Print results
filteredMap |> Map.iter (fun key value ->
   printf "Key = %A has matching list of values = %A\n" key value)

哪些打印出来

Key = "1" has matching list of values = [29; 30; 41; 48; 64]

注意事项:

  • 一定要确保处理掉 一次性 资源,如 StreamReader 在你完成它们之后。 的 use 关键字确保在资源超出范围时自动发生。

  • 如果您希望搜索一个 无序集 的值,您可以使用 set 而不是 list:

    let requiredValues = set [64;29;30;41;48] // Or whatever set of values you are looking for
    
    let map = jsonFromFile<Map<string, Set<int>>>(fileName, null)
    
    let filteredMap = 
        map |> Map.toSeq
            |> Seq.filter (fun (key, value) -> requiredValues = value)
            |> Map.ofSeq
    
  • 解释如下: F#中的平等和比较约束条件 唐-西姆,两人 listset 支持结构性平等比较,这就是为什么 requiredValues = value 检查该系列是否有相同的 内容.

演示小提琴#1 此处 对于 list 和#2 此处 对于 set.

© www.soinside.com 2019 - 2024. All rights reserved.