Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 114 additions & 17 deletions src/FsPickler/FsPickler/FsPickler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,16 @@ type FsPickler private () =
static member CreateXmlSerializer([<O;D(null)>]?typeConverter : ITypeNameConverter, [<O;D(null)>]?indent : bool, [<O;D(null)>] ?picklerResolver : IPicklerResolver) =
new XmlSerializer(?typeConverter = typeConverter, ?indent = indent, ?picklerResolver = picklerResolver)

/// Decides if given type is serializable by FsPickler
/// <summary>
/// Decides if given type is serializable by FsPickler
/// </summary>
static member IsSerializableType<'T> () : bool =
resolver().IsSerializable<'T> ()

/// Decides if given type is serializable by FsPickler
/// <summary>
/// Decides if given type is serializable by FsPickler
/// </summary>
/// <param name="t">Type to be checked.</param>
static member IsSerializableType (t : Type) : bool =
resolver().IsSerializable t

Expand All @@ -48,11 +53,16 @@ type FsPickler private () =
try FsPickler.EnsureSerializable(graph, ?failOnCloneableOnlyTypes = failOnCloneableOnlyTypes) ; true
with :? FsPicklerException -> false

/// Auto generates a pickler for given type variable
/// <summary>
/// Auto generates a pickler for given type variable
/// </summary>
static member GeneratePickler<'T> () : Pickler<'T> =
resolver().Resolve<'T> ()

/// Auto generates a pickler for given type

/// <summary>
/// Auto generates a pickler for given type
/// </summary>
/// <param name="t">Type for which pickler will be generated.</param>
static member GeneratePickler (t : Type) : Pickler =
resolver().Resolve t

Expand All @@ -69,8 +79,20 @@ type FsPickler private () =
/// <param name="pickler">Pickler used for cloning. Defaults to auto-generated pickler.</param>
/// <param name="streamingContext">Streaming context used for cloning. Defaults to null streaming context.</param>
static member Clone<'T> (value : 'T, [<O;D(null)>]?pickler : Pickler<'T>, [<O;D(null)>]?streamingContext : StreamingContext) : 'T =
let pickler = match pickler with None -> resolver().Resolve<'T> () | Some p -> p
let state = new CloneState(resolver(), ?streamingContext = streamingContext)
FsPickler.Clone(value, resolver(), ?pickler = pickler, ?streamingContext = streamingContext)

/// <summary>
/// Performs an in-memory, deep cloning of provided serializable object graph.
/// Cloning is performed on a node-to-node basis and does not make use of intermediate
/// serialization buffers.
/// </summary>
/// <param name="value">Value to be cloned.</param>
/// <param name="picklerResolver">Specify a custom pickler resolver/cache for serialization.</param>
/// <param name="pickler">Pickler used for cloning. Defaults to auto-generated pickler.</param>
/// <param name="streamingContext">Streaming context used for cloning. Defaults to null streaming context.</param>
static member Clone<'T> (value : 'T, picklerResolver : IPicklerResolver, [<O;D(null)>]?pickler : Pickler<'T>, [<O;D(null)>]?streamingContext : StreamingContext) : 'T =
let pickler = match pickler with None -> picklerResolver.Resolve<'T> () | Some p -> p
let state = new CloneState(picklerResolver, ?streamingContext = streamingContext)
pickler.Clone state value

/// <summary>
Expand All @@ -83,8 +105,21 @@ type FsPickler private () =
/// <param name="streamingContext">Streaming context used for cloning. Defaults to null streaming context.</param>
/// <returns>A sifted wrapper together with all objects that have been sifted.</returns>
static member Sift<'T>(value : 'T, sifter : IObjectSifter, [<O;D(null)>]?pickler : Pickler<'T>, [<O;D(null)>]?streamingContext : StreamingContext) : Sifted<'T> * (int64 * obj) [] =
let pickler = match pickler with None -> resolver().Resolve<'T> () | Some p -> p
let state = new CloneState(resolver(), ?streamingContext = streamingContext, sifter = sifter)
FsPickler.Sift(value, sifter, resolver(), ?pickler = pickler, ?streamingContext = streamingContext)

/// <summary>
/// Creates a clone of the provided object graph, sifting objects from the graph as specified by the provided sifter implementation.
/// Only reference types can be sifted from a graph.
/// </summary>
/// <param name="value">Value to be sifted.</param>
/// <param name="sifter">Sifting predicate implementation.</param>
/// <param name="picklerResolver">Specify a custom pickler resolver/cache for serialization.</param>
/// <param name="pickler">Pickler to be used for traversal. Defaults to auto-generated pickler.</param>
/// <param name="streamingContext">Streaming context used for cloning. Defaults to null streaming context.</param>
/// <returns>A sifted wrapper together with all objects that have been sifted.</returns>
static member Sift<'T>(value : 'T, sifter : IObjectSifter, picklerResolver : IPicklerResolver, [<O;D(null)>]?pickler : Pickler<'T>, [<O;D(null)>]?streamingContext : StreamingContext) : Sifted<'T> * (int64 * obj) [] =
let pickler = match pickler with None -> picklerResolver.Resolve<'T> () | Some p -> p
let state = new CloneState(picklerResolver, ?streamingContext = streamingContext, sifter = sifter)
let sifted = pickler.Clone state value
state.CreateSift(sifted)

Expand All @@ -98,8 +133,21 @@ type FsPickler private () =
/// <param name="streamingContext">Streaming context used for cloning. Defaults to null streaming context.</param>
/// <returns>A sifted wrapper together with all objects that have been sifted.</returns>
static member Sift<'T>(value : 'T, sifter : obj -> bool, [<O;D(null)>]?pickler : Pickler<'T>, [<O;D(null)>]?streamingContext : StreamingContext) : Sifted<'T> * (int64 * obj) [] =
FsPickler.Sift(value, sifter, resolver(), ?pickler = pickler, ?streamingContext = streamingContext)

/// <summary>
/// Creates a clone of the provided object graph, sifting objects from the graph as specified by the provided sifter implementation.
/// Only reference types can be sifted from a graph.
/// </summary>
/// <param name="value">Value to be sifted.</param>
/// <param name="sifter">Sifting predicate implementation.</param>
/// <param name="picklerResolver">Specify a custom pickler resolver/cache for serialization.</param>
/// <param name="pickler">Pickler to be used for traversal. Defaults to auto-generated pickler.</param>
/// <param name="streamingContext">Streaming context used for cloning. Defaults to null streaming context.</param>
/// <returns>A sifted wrapper together with all objects that have been sifted.</returns>
static member Sift<'T>(value : 'T, sifter : obj -> bool, picklerResolver : IPicklerResolver, [<O;D(null)>]?pickler : Pickler<'T>, [<O;D(null)>]?streamingContext : StreamingContext) : Sifted<'T> * (int64 * obj) [] =
let sifter = { new IObjectSifter with member __.Sift(_,_,t) = sifter t }
FsPickler.Sift(value, sifter, ?pickler = pickler, ?streamingContext = streamingContext)
FsPickler.Sift(value, sifter, picklerResolver, ?pickler = pickler, ?streamingContext = streamingContext)

/// <summary>
/// Unsifts a provided object graph with given values.
Expand All @@ -114,6 +162,20 @@ type FsPickler private () =
let state = new CloneState(resolver(), ?streamingContext = streamingContext, unSiftData = (values, sifted.SiftedIndices))
pickler.Clone state sifted.Value

/// <summary>
/// Unsifts a provided object graph with given values.
/// </summary>
/// <param name="sifted">Sifted object graph to be unsifted.</param>
/// <param name="values">Values to be pushed in sift holes.</param>
/// <param name="picklerResolver">Specify a custom pickler resolver/cache for serialization.</param>
/// <param name="pickler">Pickler to be used for traversal. Defaults to auto-generated pickler.</param>
/// <param name="streamingContext">Streaming context used for cloning. Defaults to null streaming context.</param>
/// <returns>An unsifted object graph.</returns>
static member UnSift<'T>(sifted : Sifted<'T>, values:(int64 * obj) [], picklerResolver : IPicklerResolver, [<O;D(null)>]?pickler : Pickler<'T>, [<O;D(null)>]?streamingContext : StreamingContext) : 'T =
let pickler = match pickler with None -> picklerResolver.Resolve<'T> () | Some p -> p
let state = new CloneState(picklerResolver, ?streamingContext = streamingContext, unSiftData = (values, sifted.SiftedIndices))
pickler.Clone state sifted.Value

/// <summary>Compute size in bytes for given input.</summary>
/// <param name="value">input value.</param>
/// <param name="pickler">Pickler to be used for size computation. Defaults to auto-generated pickler.</param>
Expand All @@ -137,10 +199,21 @@ type FsPickler private () =
/// <param name="visitOrder">Object graph traversal order. Defaults to pre-order traversal.</param>
static member VisitObject(visitor : IObjectVisitor, graph : 'T, [<O;D(null)>]?pickler:Pickler<'T>,
[<O;D(null)>]?streamingContext:StreamingContext, [<O;D(null)>]?visitOrder:VisitOrder) =
FsPickler.VisitObject(visitor, graph, resolver(), ?streamingContext = streamingContext, ?visitOrder = visitOrder)

let resolver = resolver()
let pickler = match pickler with None -> resolver.Resolve<'T> () | Some p -> p
let state = new VisitState(resolver, visitor, ?streamingContext = streamingContext, ?visitOrder = visitOrder)
/// <summary>
/// Visits all reference types that appear in the given object graph.
/// </summary>
/// <param name="visitor">Visitor implementation.</param>
/// <param name="graph">Object graph.</param>
/// <param name="picklerResolver">Specify a custom pickler resolver/cache for serialization.</param>
/// <param name="pickler">Pickler to be used for traversal. Defaults to auto-generated pickler.</param>
/// <param name="streamingContext">Streaming context used for cloning. Defaults to null streaming context.</param>
/// <param name="visitOrder">Object graph traversal order. Defaults to pre-order traversal.</param>
static member VisitObject(visitor : IObjectVisitor, graph : 'T, picklerResolver : IPicklerResolver, [<O;D(null)>]?pickler:Pickler<'T>,
[<O;D(null)>]?streamingContext:StreamingContext, [<O;D(null)>]?visitOrder:VisitOrder) =
let pickler = match pickler with None -> picklerResolver.Resolve<'T> () | Some p -> p
let state = new VisitState(picklerResolver, visitor, ?streamingContext = streamingContext, ?visitOrder = visitOrder)
pickler.Accept state graph

/// <summary>Compute size and hashcode for given input.</summary>
Expand All @@ -154,6 +227,14 @@ type FsPickler private () =
/// </summary>
/// <param name="graph">input object graph.</param>
static member GatherTypesInObjectGraph(graph : obj) : Type [] =
FsPickler.GatherTypesInObjectGraph(graph, resolver())

/// <summary>
/// Uses FsPickler to traverse the object graph, gathering types of objects as it goes.
/// </summary>
/// <param name="graph">input object graph.</param>
/// <param name="picklerResolver">Specify a custom pickler resolver/cache for serialization.</param>
static member GatherTypesInObjectGraph(graph : obj, picklerResolver : IPicklerResolver) : Type [] =
let gathered = new HashSet<Type> ()
let visitor =
{
Expand All @@ -179,14 +260,22 @@ type FsPickler private () =
true // always continue traversal
}

do FsPickler.VisitObject(visitor, graph)
do FsPickler.VisitObject(visitor, graph, picklerResolver)
gathered |> Seq.toArray

/// <summary>
/// Use FsPickler to traverse the object graph, gathering object instances as it goes.
/// </summary>
/// <param name="graph">input object graph.</param>
static member GatherObjectsInGraph (graph : obj) : obj [] =
FsPickler.GatherObjectsInGraph(graph, resolver())

/// <summary>
/// Use FsPickler to traverse the object graph, gathering object instances as it goes.
/// </summary>
/// <param name="graph">input object graph.</param>
/// <param name="picklerResolver">Specify a custom pickler resolver/cache for serialization.</param>
static member GatherObjectsInGraph (graph : obj, picklerResolver : IPicklerResolver) : obj [] =
let gathered = new HashSet<obj> ()
let visitor =
{
Expand All @@ -201,16 +290,24 @@ type FsPickler private () =
true // continue traversal
}

do FsPickler.VisitObject(visitor, graph)
do FsPickler.VisitObject(visitor, graph, picklerResolver)
gathered |> Seq.toArray


/// <summary>
/// Traverses the object graph, completing if serializable or raising a serialization exception if not.
/// </summary>
/// <param name="graph">Graph to be checked.</param>
/// <param name="failOnCloneableOnlyTypes">Fail on types that are declared cloneable only. Defaults to true.</param>
static member EnsureSerializable (graph : 'T, [<O;D(null)>] ?failOnCloneableOnlyTypes : bool) : unit =
FsPickler.EnsureSerializable(graph, resolver(), ?failOnCloneableOnlyTypes = failOnCloneableOnlyTypes)

/// <summary>
/// Traverses the object graph, completing if serializable or raising a serialization exception if not.
/// </summary>
/// <param name="graph">Graph to be checked.</param>
/// <param name="picklerResolver">Specify a custom pickler resolver/cache for serialization.</param>
/// <param name="failOnCloneableOnlyTypes">Fail on types that are declared cloneable only. Defaults to true.</param>
static member EnsureSerializable (graph : 'T, picklerResolver : IPicklerResolver, [<O;D(null)>] ?failOnCloneableOnlyTypes : bool) : unit =
let failOnCloneableOnlyTypes = defaultArg failOnCloneableOnlyTypes true
let visitor =
{ new IObjectVisitor with
Expand All @@ -220,4 +317,4 @@ type FsPickler private () =
else
true }

FsPickler.VisitObject(visitor, graph, visitOrder = VisitOrder.PreOrder)
FsPickler.VisitObject(visitor, graph, picklerResolver, visitOrder = VisitOrder.PreOrder)