fsprojects / FSharp.Data

F# Data: Library for Data Access
https://fsprojects.github.io/FSharp.Data
Other
806 stars 288 forks source link

JsonProvider - writable properties? #1465

Open JordanMarr opened 1 year ago

JordanMarr commented 1 year ago

Is it possible for the JsonProvider to generate types with writable properties?

My use case is that I have a rather large JSON sample object that I want to create a new instance of using the default values in my sample and then manually modify only a few select fields. However, the read-only nature of the generated properties prevents me from using this technique, leaving only the option of initializing every value via the constructor.

JordanMarr commented 1 year ago

I came up with the following workaround to allow editing individual fields:

Set Value Extension Methods

module Extensions

open FSharp.Data
open FSharp.Data.JsonExtensions

/// Extension methods to allow overwriting read-only properties on generated types.
type JsonValue with 

    member private this.SetValue(jsonProperty: string, value: JsonValue) = 
        let idx = this.Properties |> Array.findIndex (fun (pNm, _) -> pNm = jsonProperty)
        this.Properties[idx] <- jsonProperty, value

    /// Sets a json property on a generated type.
    member this.Set(jsonProperty: string, value: JsonValue) = 
        this.SetValue(jsonProperty, value)

    /// Sets a json property on a generated type.
    member this.Set(jsonProperty: string, value: string) = 
        this.SetValue(jsonProperty, JsonValue.String value)

    /// Sets a json property on a generated type.
    member this.Set(jsonProperty: string, value: decimal) = 
        this.SetValue(jsonProperty, JsonValue.Number value)

    /// Sets a json property on a generated type.
    member this.Set(jsonProperty: string, value: float) = 
        this.SetValue(jsonProperty, JsonValue.Float value)

    /// Sets a json property on a generated type.
    member this.Set(jsonProperty: string, value: bool) = 
        this.SetValue(jsonProperty, JsonValue.Boolean value)

Usage


[<Test>]
let ``Create RFI Entity`` () = 
    let rfi = RfiTypes.GetSample()
    rfi.Subject =! "Wall Color"

    // manually overwrite a read-only property value
    rfi.JsonValue.Set("subject", "New Subject")

    rfi.Subject =! "New Subject"

It's not perfect as I lose some of the benefits of having strongly typed entities; however, it still saves me from having to manually generate large entities, and since I only need to edit a handful of properties, it seems like a pretty good workaround.

It would be a lot more strongly typed if there was an option of having writable properties though.