@@ -16,6 +16,7 @@ type internal RecordProperty =
1616 MustBeNonNull: bool
1717 MustBePresent: bool
1818 IsSkip: obj -> bool
19+ WriteOrder: int
1920 }
2021
2122type internal IRecordConverter =
@@ -28,9 +29,34 @@ type JsonRecordConverter<'T>(options: JsonSerializerOptions, fsOptions: JsonFSha
2829
2930 let recordType : Type = typeof< 'T>
3031
32+ let fields = FSharpType.GetRecordFields( recordType, true )
33+
34+ let fieldOrderIndices =
35+ let revIndices =
36+ fields
37+ |> Array.mapi ( fun i field ->
38+ let revI =
39+ match field.GetCustomAttributes( typeof< JsonPropertyOrderAttribute>, true ) with
40+ | [| :? JsonPropertyOrderAttribute as attr |] -> attr.Order
41+ | _ -> 0
42+ struct ( revI, i))
43+ if revIndices |> Array.exists ( fun struct ( revI , _ ) -> revI <> 0 ) then
44+ // Using Seq.sort rather than Array.sort because it is stable
45+ let revIndices =
46+ revIndices
47+ |> Seq.sortBy ( fun struct ( revI , _ ) -> revI)
48+ |> Array.ofSeq
49+ let res = Array.zeroCreate fields.Length
50+ for i in 0 .. res.Length-1 do
51+ let struct ( _ , x ) = revIndices[ i]
52+ res[ x] <- i
53+ ValueSome res
54+ else
55+ ValueNone
56+
3157 let fieldProps =
32- FSharpType.GetRecordFields ( recordType , true )
33- |> Array.map ( fun p ->
58+ fields
59+ |> Array.mapi ( fun i p ->
3460 let name =
3561 match p.GetCustomAttributes( typeof< JsonPropertyNameAttribute>, true ) with
3662 | [| :? JsonPropertyNameAttribute as name |] -> name.Name
@@ -57,9 +83,15 @@ type JsonRecordConverter<'T>(options: JsonSerializerOptions, fsOptions: JsonFSha
5783 MustBeNonNull = not canBeNull
5884 MustBePresent = not canBeSkipped
5985 IsSkip = isSkip p.PropertyType
86+ WriteOrder = match fieldOrderIndices with ValueSome a -> a[ i] | ValueNone -> i
6087 }
6188 )
6289
90+ let writeOrderedFieldProps =
91+ let a = Array.mapi ( fun i x -> struct ( i, x)) fieldProps
92+ a |> Array.sortInPlaceBy ( fun struct ( _ , x ) -> x.WriteOrder)
93+ a
94+
6395 let fieldCount = fieldProps.Length
6496 let minExpectedFieldCount =
6597 fieldProps
@@ -150,9 +182,8 @@ type JsonRecordConverter<'T>(options: JsonSerializerOptions, fsOptions: JsonFSha
150182
151183 member internal _.WriteRestOfObject ( writer , value , options ) =
152184 let values = dector value
153- for i in 0 .. fieldProps.Length -1 do
185+ for struct ( i , p ) in writeOrderedFieldProps do
154186 let v = values[ i]
155- let p = fieldProps[ i]
156187 if not p.Ignore && not ( options.IgnoreNullValues && isNull v) && not ( p.IsSkip v) then
157188 writer.WritePropertyName( p.Name)
158189 JsonSerializer.Serialize( writer, v, p.Type, options)
0 commit comments