Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I serialize an F# record without having it output duplicate fields with @ signs at the end? #1034

Open
mrakgr opened this issue Jan 17, 2025 · 7 comments

Comments

@mrakgr
Copy link

mrakgr commented Jan 17, 2025

Example code:

#r "nuget: YamlDotNet, 16.3.0"
open YamlDotNet
open System

[<CLIMutable>]
type t = {
    qwer : string
    asdf : int
}

let t = {
    qwer = "hello"
    asdf = 1234
}

let serializer = new Serialization.Serializer()
serializer.Serialize(Console.Out, t)

Here is what it outputs when I run it.

qwer@: hello
asdf@: 1234
qwer: hello
asdf: 1234

How do I prevent it from generating those extraneus duplicate fields ending in @?

@mrakgr
Copy link
Author

mrakgr commented Jan 17, 2025

Here is another example. The results this library is giving me are bonkers.

#r "nuget: YamlDotNet, 16.3.0"
open YamlDotNet.Serialization
open YamlDotNet.Serialization.NamingConventions

[<CLIMutable>]
type t = {
    Qwerty : string
    Asdf : int
}

let t = {
    Qwerty = "hello"
    Asdf = 1234
}

let serializer =
    SerializerBuilder()
        .WithNamingConvention(CamelCaseNamingConvention.Instance)
        .Build()

serializer.Serialize([|t;t;t|])
|> printfn "%s"

Running the script with dotnet fsi gives me:

- &o0
  qwerty@: hello
  asdf@: 1234
  qwerty: hello
  asdf: 1234
- *o0
- *o0

@mrakgr
Copy link
Author

mrakgr commented Jan 17, 2025

The library does have tests that look sensible, so I am confused what the problem is.

@EdwardCooke
Copy link
Collaborator

The library uses reflection to determine what to put in the yaml. Fsharp likes to make weird properties on its types, I think it was optional fields that caused us some grief. My guess is this is something that fsharp is now doing. To get around it you could make a custom type converter which will allow you to generate and read the yaml exactly how you want.

@pewill
Copy link

pewill commented Jan 18, 2025

@mrakgr can you try running the sample code in a console app. I only see the behavior you reported when running the code in FSX but not with a .NET console app.

@mrakgr
Copy link
Author

mrakgr commented Jan 18, 2025

@mrakgr can you try running the sample code in a console app. I only see the behavior you reported when running the code in FSX but not with a .NET console app.

At works I am running a bunch of F# scripts for convenience, and it'd be inconvenient to convert them into a project.

@mrakgr
Copy link
Author

mrakgr commented Jan 18, 2025

The library uses reflection to determine what to put in the yaml. Fsharp likes to make weird properties on its types, I think it was optional fields that caused us some grief. My guess is this is something that fsharp is now doing. To get around it you could make a custom type converter which will allow you to generate and read the yaml exactly how you want.

Month or two ago, I looked into this library, ran into the same issues and made my own Yaml serializer, I literally couldn't find a library for it out there. The reason why I decided to actually ask about this issue now is because I've noticed that you can serialize F# Maps (and mine can't), and you also have deserialization capabilities, which I needed. The way I got around the issues with serializing the records in my own serializer is by using the Fsharp reflection functions...

            if FSharpType.IsRecord t then
                let record_field_names = FSharpType.GetRecordFields(x.GetType()) |> Array.map (fun x -> x.Name)
                let record_fields = FSharpValue.GetRecordFields x |> Seq.map serialize_to_repr
                Seq.zip record_field_names record_fields |> Record
            elif FSharpType.IsTuple t then
                FSharpValue.GetTupleFields x |> Seq.map serialize_to_repr |> Seq

This'll give you exactly the record fields without the extras. The other case also shows how you can get all the items of a tuple in an array. Right now, the library isn't serializing them as a list.

The F# script that I made is getting rewritten into Python by a coworker, so I am not going to pursue this issue further, and I am sorry that I won't have time to dive into the library to fix this.

@hez2010
Copy link

hez2010 commented Jan 19, 2025

The library uses reflection to determine what to put in the yaml. Fsharp likes to make weird properties on its types, I think it was optional fields that caused us some grief. My guess is this is something that fsharp is now doing. To get around it you could make a custom type converter which will allow you to generate and read the yaml exactly how you want.

I think it's better to filter out those unspeaking properties in the library. qwerty@ is definitely not a name that can be represented in ordinary code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants