2012-04-04 2 views
1

J'ai besoin d'une fonction pour convertir les types d'une bibliothèque tierce en IDictionary s afin qu'ils puissent être facilement sérialisés (en JSON). Il existe des dépendances entre les types, de sorte que les dictionnaires sont parfois imbriqués.Extension des types dans la bibliothèque tierce pour la sérialisation

En ce moment j'ai quelque chose de hideux comme ceci:

//Example type 
type A(name) = 
    member __.Name = name 

//Example type 
type B(name, alist) = 
    member __.Name = name 
    member __.AList : A list = alist 

let rec ToSerializable x = 
    match box x with 
    | :? A as a -> dict ["Name", box a.Name] 
    | :? B as b -> dict ["Name", box b.Name; "AList", box (List.map ToSerializable b.AList)] 
    | _ -> failwith "wrong type" 

Cela transformerait tout à un type primitif, un IEnumerable d'un tel type, ou un dictionnaire.

Cette fonction continuera de croître au fur et à mesure de l'ajout de types (pouah). Ce n'est pas sécurisé pour la saisie de type (nécessitant le schéma fourre-tout). Déterminer quels types sont pris en charge nécessite de parcourir la correspondance de modèle monolithique.

J'aimerais pouvoir faire ceci:

type ThirdPartyType with 
    member x.ToSerializable() = ... 

let inline toSerializable x = 
    (^T : (member ToSerializable : unit -> IDictionary<string,obj>) x) 

let x = ThirdPartyType() |> toSerializable //type extensions don't satisfy static member constraints 

Donc, je suis à la recherche de créativité ici. Y a-t-il une meilleure façon d'écrire ceci qui répond à mes plaintes?

Répondre

3

est ici une solution qui répond à la question de type sécurité, mais pas nécessairement votre question d'extensibilité:

// these types can appear in any assemblies 
type A = { Name : string } 
type B = { Name : string; AList : A list } 
type C(name:string) = 
    member x.Name = name 
    static member Serialize(c:C) = dict ["Name", box c.Name] 

 

// all of the following code goes in one place 
open System.Collections.Generic 

type SerializationFunctions = class end 

let inline serializationHelper< ^s, ^t when (^s or ^t) : (static member Serialize : ^t -> IDictionary<string,obj>)> t = 
    ((^s or ^t) : (static member Serialize : ^t -> IDictionary<string,obj>) t) 
let inline serialize t = serializationHelper<SerializationFunctions,_> t 

// overloads for each type that doesn't define its own Serialize method 
type SerializationFunctions with 
    static member Serialize (a:A) = dict ["Name", box a.Name] 
    static member Serialize (b:B) = dict ["Name", box b.Name; "AList", box (List.map serialize b.AList)] 

let d1 = serialize { A.Name = "a" } 
let d2 = serialize { B.Name = "b"; AList = [{ A.Name = "a" }]} 
let d3 = serialize (C "test") 
+0

+1 C'est une utilisation intelligente des contraintes de membres statiques! – Daniel

+0

Malheureusement, je ne peux pas faire 'x.Items |> Array.map (unbox >> sérialiser) |> box'. Il semble qu'il n'y ait aucun moyen de transformer en '^ t' où'^t requiert un membre ToSerializable'. – Daniel

+0

@Daniel - à droite, parce que 't 'doit être connu statiquement, donc cela n'a pas de sens de le faire à l'exécution. Vous pourriez potentiellement utiliser la réflexion à la place. – kvb

2

comme idée rapide n évidente: l'utilisation surchargent

//Example type 
type A(name) = 
    member __.Name = name 

//Example type 
type B(name, alist) = 
    member __.Name = name 
    member __.AList : A list = alist 

type Converter = 
    static member ToSerializable(a : A) = dict ["Name", box a.Name] 
    static member ToSerializable(b : B) = dict ["Name", box b.Name; "AList", box (b.AList |> List.map Converter.ToSerializable)] 
+0

C'est marrant, cela aurait probablement été évident pour moi il y a plusieurs années. Mon aventure dans la programmation fonctionnelle a apparemment un départ correspondant à OO. – Daniel

Questions connexes