F # 대 C #의 역 직렬화

도마

다음 json이 있습니다.

{
  "data": [
    {
      "timestamp": "2019-11-07T00:23:52.095Z",
      "symbol": "XBTUSD",
      "side": "Buy",
      "size": 1,
      "price": 9356.5,
      "tickDirection": "PlusTick",
      "trdMatchID": "01476235-ad89-1777-9067-8ce6d0e29992",
      "grossValue": 10688,
      "homeNotional": 0.00010688,
      "foreignNotional": 1
    }
  ]
}

마지막 3 개 필드는 선택 사항입니다.

C #에서 역 직렬화 할 때 다음을 수행합니다.

public class Trade
{
    public DateTime Timestamp;
    public string Symbol;
    public string Side;
    public long Size;
    public long Price;
    public long? GrossValue;
    public float? HomeNotional;
    public float? ForeignNotional;
}

public class TradeContainer
{
    public Trade[] Data;
}

var j = JsonConvert.DeserializeObject<TradeContainer>(x);

그리고 그것은 모두 좋습니다.

F #에서는 다음을 수행합니다.

type Trade =
    {
        Timestamp : DateTime
        Symbol : string
        Side : string
        Size : int64
        Price : int64
        GrossValue : int64 option
        HomeNotional : float option
        ForeignNotional : float option
    }

type TradeContainer =
    {
        Data : Trade[]
    }

let t = JsonConvert.DeserializeObject<TradeContainer>(x)

그러나 그것은 실패 할 것입니다. 그러나 Trade type에서 option 키워드를 제거하면 제대로 deserialize됩니다.

내가 얻는 오류는 다음과 같습니다.

Newtonsoft.Json.JsonSerializationException : Union을 읽을 때 예기치 않은 속성 'homeNotional'이 발견되었습니다. 경로 'data [0] .homeNotional', 줄 1, 위치 233. at Newtonsoft.Json.Converters.DiscriminatedUnionConverter.ReadJson (JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)

왜 다른가요? 일부 값이 없을 수도 있다는 계획을 세워야하므로 어떻게이 작업을 수행 할 수 있습니까?

파나지오티스 카나 보스

문제는 JSON.NET의 DU 직렬화 구현에 있습니다. 관용적이지 않으며 본질적으로 케이스와 필드를 덤프합니다.

type Shape =
    | Rectangle of width : float * length : float
    | Circle of radius : float
    | Empty

[<EntryPoint>]
let main argv = 

    let shape1 = Rectangle(1.3, 10.0)

    let json = JsonConvert.SerializeObject(shape1)
    // {
    //   "Case": "Rectangle",
    //   "Fields": [
    //     1.3,
    //     10.0
    //   ]
    // }

deserializer는 동일한 구조를 예상합니다.

Isaac Abraham 은 대신 사용해야 하는 관용적 사용자 지정 변환기만들었습니다 .

let settings = new JsonSerializerSettings()
settings.Converters.Add(IdiomaticDuConverter())

let t = JsonConvert.DeserializeObject<TradeContainer>(json,settings)

IdiomaticDuConverter의 코드는 다음과 같습니다.

namespace Newtonsoft.Json.Converters

open Microsoft.FSharp.Reflection
open Newtonsoft.Json
open System

type IdiomaticDuConverter() = 
    inherit JsonConverter()

    [<Literal>]
    let discriminator = "__Case"
    let primitives = Set [ JsonToken.Boolean; JsonToken.Date; JsonToken.Float; JsonToken.Integer; JsonToken.Null; JsonToken.String ]

    let writeValue (value:obj) (serializer:JsonSerializer, writer : JsonWriter) =
        if value.GetType().IsPrimitive then writer.WriteValue value
        else serializer.Serialize(writer, value)

    let writeProperties (fields : obj array) (serializer:JsonSerializer, writer : JsonWriter) = 
        fields |> Array.iteri (fun index value -> 
                      writer.WritePropertyName(sprintf "Item%d" index)
                      (serializer, writer) |> writeValue value)

    let writeDiscriminator (name : string) (writer : JsonWriter) = 
        writer.WritePropertyName discriminator
        writer.WriteValue name

    override __.WriteJson(writer, value, serializer) = 
        let unionCases = FSharpType.GetUnionCases(value.GetType())
        let unionType = value.GetType()
        let case, fields = FSharpValue.GetUnionFields(value, unionType)
        let allCasesHaveValues = unionCases |> Seq.forall (fun c -> c.GetFields() |> Seq.length > 0)

        match unionCases.Length, fields, allCasesHaveValues with
        | 2, [||], false -> writer.WriteNull()
        | 1, [| singleValue |], _
        | 2, [| singleValue |], false -> (serializer, writer) |> writeValue singleValue
        | 1, fields, _
        | 2, fields, false -> 
            writer.WriteStartObject()
            (serializer, writer) |> writeProperties fields
            writer.WriteEndObject()
        | _ -> 
            writer.WriteStartObject()
            writer |> writeDiscriminator case.Name
            (serializer, writer) |> writeProperties fields
            writer.WriteEndObject()

    override __.ReadJson(reader, destinationType, _, _) = 
        let parts = 
            if reader.TokenType <> JsonToken.StartObject then [| (JsonToken.Undefined, obj()), (reader.TokenType, reader.Value) |]
            else 
                seq { 
                    yield! reader |> Seq.unfold (fun reader -> 
                                         if reader.Read() then Some((reader.TokenType, reader.Value), reader)
                                         else None)
                }
                |> Seq.takeWhile(fun (token, _) -> token <> JsonToken.EndObject)
                |> Seq.pairwise
                |> Seq.mapi (fun id value -> id, value)
                |> Seq.filter (fun (id, _) -> id % 2 = 0)
                |> Seq.map snd
                |> Seq.toArray

        let values = 
            parts
            |> Seq.filter (fun ((_, keyValue), _) -> keyValue <> (discriminator :> obj))
            |> Seq.map snd
            |> Seq.filter (fun (valueToken, _) -> primitives.Contains valueToken)
            |> Seq.map snd
            |> Seq.toArray

        let case = 
            let unionCases = FSharpType.GetUnionCases(destinationType)
            let unionCase =
                parts
                |> Seq.tryFind (fun ((_,keyValue), _) -> keyValue = (discriminator :> obj))
                |> Option.map (snd >> snd)
            match unionCase with
            | Some case -> unionCases |> Array.find (fun f -> f.Name :> obj = case)
            | None ->
                // implied union case
                match values with
                | [| null |] -> unionCases |> Array.find(fun c -> c.GetFields().Length = 0)
                | _ -> unionCases |> Array.find(fun c -> c.GetFields().Length > 0)

        let values = 
            case.GetFields()
            |> Seq.zip values
            |> Seq.map (fun (value, propertyInfo) -> Convert.ChangeType(value, propertyInfo.PropertyType))
            |> Seq.toArray

        FSharpValue.MakeUnion(case, values)

    override __.CanConvert(objectType) =
        FSharpType.IsUnion objectType &&
        not (objectType.IsGenericType &&
             typedefof<list<_>> = objectType.GetGenericTypeDefinition())

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

c #-특정 JSON의 역 직렬화

분류에서Dev

C # 역 직렬화

분류에서Dev

C # / JSON 개체 직렬화, 역 직렬화 및 대 / 소문자 문제 없음

분류에서Dev

F #에서 json.net의 역 직렬화 문제

분류에서Dev

C # 개체 오류에 대한 JSON 응답 역 직렬화

분류에서Dev

C #에서 JSON 역 직렬화 : 개체 대 배열

분류에서Dev

Java OOP의 역 직렬화

분류에서Dev

자바의 역 직렬화

분류에서Dev

자바의 역 직렬화

분류에서Dev

JSON C # 역 직렬화

분류에서Dev

c # Json 역 직렬화 식

분류에서Dev

C # : JSON 역 직렬화

분류에서Dev

JSON C # 역 직렬화

분류에서Dev

JSON C # 역 직렬화

분류에서Dev

사용자 지정 클래스의 개체에 대한 NSMutableArray의 직렬화 및 역 직렬화

분류에서Dev

JSON 문자열을 C # 및 Unity의 개체로 역 직렬화

분류에서Dev

C #에서 크기 1의 JSON 배열 역 직렬화

분류에서Dev

다중 자식 C #의 역 직렬화 XML

분류에서Dev

(역) C #에서 변경된 개체의 직렬화

분류에서Dev

C #의 클래스로 json 역 직렬화

분류에서Dev

.NET의 필드를 기반으로 json 역 직렬화 (C #)

분류에서Dev

C #에서 다른 종류의 XML 역 직렬화

분류에서Dev

C #의 목록이있는 개체에 XML 역 직렬화

분류에서Dev

C #의 특정 수준에서 역 직렬화

분류에서Dev

C #의 JSON 배열을 목록으로 역 직렬화

분류에서Dev

C # 임의 키로 시작하는 Facebook JSON 역 직렬화

분류에서Dev

C # Xml 역 직렬화, XML의 데이터 무시

분류에서Dev

XML을 C #의 목록으로 역 직렬화

분류에서Dev

중첩 된 목록의 XML 역 직렬화 C #