Search code examples
c#jsonf#type-providersf#-data

Playing with Type Provider and JSON API


I am trying to consume an API which return a JSON built as :

Team.player1 / player2 / player3... (the players are built as properties and not as an array)

I was thinking to use reflection to do it, but having hard time to get a player.

type Simple = JsonProvider<"sample.json">

let wbReq = "API-FOR-TEAM"    

let docAsync = Simple.Load(wbReq)

let allValues = FSharpType.GetRecordFields (docAsync.Team.Players.GetType())
let test = allValues
            |> Seq.map (fun p -> (p.GetValue(docAsync.Team.Players) as ?).Name) // HOW TO GET THE TYPED PLAYER HERE ?
            |> fun p -> printfn p

EDIT : I tried to use GetType and System.Convert.ChangeType

EDIT2 : Here is a simplified version of the JSON :

{
    "Team": {
        "id": "8",
        "players": {
            "17878": {
                "info": {
                    "idteam": 8,
                    "idplayer": 17878,
                    "note": 6
                }
            },
            "18507": {
                "info": {
                    "idteam": 8,
                    "idplayer": 18507,
                    "note": 5
                }
            }
        }
    }
}

Edit 3 :

I found an easy solution in C# (thanks to JSON.net and dynamic) but for learning purpose I would like to do the same in F# if someone wanna help :

        private static List<Player> Parse(string jsonString)
        {
            dynamic jsonObject = JsonConvert.DeserializeObject(jsonString);

            var players = ParseItems(jsonObject.Home.players);

            return players;
        }

        private static List<Player> ParseItems(dynamic items)
        {
            List<Player> itemList = new List<Player>();
            foreach (var item in items)
            {
                itemList.Add(new Player()
                {
                    idplayer = item.Value.info.idplayer,
                    lastName = item.Value.info.lastname,
                    note = item.Value.info.note
                });
            }
            return itemList;
        }

Solution

  • You can mix JsonTypeProvider and parsing Json. For example:

    [<Literal>]
    let sample = """{
        "Team": {
            "id": "8",
            "players": {
                "17878": {
                    "info": {
                        "idteam": 8,
                        "idplayer": 17878,
                        "note": 6
                    }
                },
                "18507": {
                    "info": {
                        "idteam": 8,
                        "idplayer": 18507,
                        "note": 5
                    }
                }
            }
        }
    }"""
    
    type Player = {IdTeam:int; IdPlayer:int; Note:int}
    
    type Simple = JsonProvider<sample>
    let docAsync = Simple.GetSample()
    
    let json = docAsync.Team.Players.JsonValue
    
    let parseInfo (json:JsonValue) = 
    
        let id_team = (json.GetProperty "idteam").AsInteger()
        let id_player = (json.GetProperty "idplayer").AsInteger()
        let note = (json.GetProperty "note").AsInteger()
    
        {IdTeam = id_team; IdPlayer = id_player; Note = note}
    
    let players = 
        json.Properties()
        |> Array.map(fun (_,x) -> x.GetProperty "info")
        |> Array.map (parseInfo)
    
    players
    |> Array.iter (printfn "%A")