// This file is part of YamlDotNet - A .NET library for YAML.
// Copyright (c) Antoine Aubry and contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
#if NET7_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
using YamlDotNet.Core;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
namespace YamlDotNet.Serialization
{
///
/// Creates and configures instances of .
/// This class is used to customize the behavior of . Use the relevant methods
/// to apply customizations, then call to create an instance of the serializer
/// with the desired customizations.
///
public sealed class StaticSerializerBuilder : StaticBuilderSkeleton
{
private readonly StaticContext context;
private readonly StaticObjectFactory factory;
private ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory;
private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories;
private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories;
private readonly LazyComponentRegistrationList eventEmitterFactories;
private readonly Dictionary tagMappings = new ();
private int maximumRecursion = 50;
private EmitterSettings emitterSettings = EmitterSettings.Default;
private DefaultValuesHandling defaultValuesHandlingConfiguration = DefaultValuesHandling.Preserve;
private bool quoteNecessaryStrings;
private bool quoteYaml1_1Strings;
private ScalarStyle defaultScalarStyle;
public StaticSerializerBuilder(StaticContext context)
: base(new DynamicTypeResolver())
{
this.context = context;
factory = context.GetFactory();
typeInspectorFactories.Add(typeof(CachedTypeInspector), inner => new CachedTypeInspector(inner));
typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), inner => namingConvention is NullNamingConvention ? inner : new NamingConventionTypeInspector(inner, namingConvention));
typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), inner => new YamlAttributesTypeInspector(inner));
preProcessingPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList, IObjectGraphVisitor>
{
{ typeof(AnchorAssigner), typeConverters => new AnchorAssigner(typeConverters) }
};
emissionPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList>
{
{
typeof(CustomSerializationObjectGraphVisitor),
args => new CustomSerializationObjectGraphVisitor(args.InnerVisitor, args.TypeConverters, args.NestedObjectSerializer)
},
{
typeof(AnchorAssigningObjectGraphVisitor),
args => new AnchorAssigningObjectGraphVisitor(args.InnerVisitor, args.EventEmitter, args.GetPreProcessingPhaseObjectGraphVisitor())
},
{
typeof(DefaultValuesObjectGraphVisitor),
args => new DefaultValuesObjectGraphVisitor(defaultValuesHandlingConfiguration, args.InnerVisitor, factory)
},
{
typeof(CommentsObjectGraphVisitor),
args => new CommentsObjectGraphVisitor(args.InnerVisitor)
}
};
eventEmitterFactories = new LazyComponentRegistrationList
{
{
typeof(TypeAssigningEventEmitter), inner =>
new TypeAssigningEventEmitter(inner,
tagMappings,
quoteNecessaryStrings,
quoteYaml1_1Strings,
defaultScalarStyle,
yamlFormatter,
enumNamingConvention,
BuildTypeInspector())
}
};
objectGraphTraversalStrategyFactory = (typeInspector, typeResolver, typeConverters, maximumRecursion) =>
new FullObjectGraphTraversalStrategy(typeInspector, typeResolver, maximumRecursion, namingConvention, factory);
}
protected override StaticSerializerBuilder Self { get { return this; } }
///
/// Put double quotes around strings that need it, for example Null, True, False, a number. This should be called before any other "With" methods if you want this feature enabled.
///
/// Also quote strings that are valid scalars in the YAML 1.1 specification (which includes boolean Yes/No/On/Off, base 60 numbers and more)
public StaticSerializerBuilder WithQuotingNecessaryStrings(bool quoteYaml1_1Strings = false)
{
quoteNecessaryStrings = true;
this.quoteYaml1_1Strings = quoteYaml1_1Strings;
return this;
}
///
/// Put double quotes around strings that need it, for example Null, True, False, a number. This should be called before any other "With" methods if you want this feature enabled.
///
public StaticSerializerBuilder WithQuotingNecessaryStrings()
{
quoteNecessaryStrings = true;
return this;
}
///
/// Sets the default quoting style for scalar values. The default value is
///
public StaticSerializerBuilder WithDefaultScalarStyle(ScalarStyle style)
{
this.defaultScalarStyle = style;
return this;
}
///
/// Sets the maximum recursion that is allowed while traversing the object graph. The default value is 50.
///
public StaticSerializerBuilder WithMaximumRecursion(int maximumRecursion)
{
if (maximumRecursion <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maximumRecursion), $"The maximum recursion specified ({maximumRecursion}) is invalid. It should be a positive integer.");
}
this.maximumRecursion = maximumRecursion;
return this;
}
///
/// Registers an additional to be used by the serializer.
///
/// A function that instantiates the event emitter.
public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory)
where TEventEmitter : IEventEmitter => WithEventEmitter(eventEmitterFactory, w => w.OnTop());
///
/// Registers an additional to be used by the serializer.
///
/// A function that instantiates the event emitter.
public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory)
where TEventEmitter : IEventEmitter => WithEventEmitter(eventEmitterFactory, w => w.OnTop());
///
/// Registers an additional to be used by the serializer.
///
/// A function that instantiates the event emitter.
/// Configures the location where to insert the
public StaticSerializerBuilder WithEventEmitter(
Func eventEmitterFactory,
Action> where
) where TEventEmitter : IEventEmitter => WithEventEmitter((IEventEmitter e, ITypeInspector _) => eventEmitterFactory(e), where);
///
/// Registers an additional to be used by the serializer.
///
/// A function that instantiates the event emitter.
/// Configures the location where to insert the
public StaticSerializerBuilder WithEventEmitter(
Func eventEmitterFactory,
Action> where
)
where TEventEmitter : IEventEmitter
{
if (eventEmitterFactory == null)
{
throw new ArgumentNullException(nameof(eventEmitterFactory));
}
if (where == null)
{
throw new ArgumentNullException(nameof(where));
}
where(eventEmitterFactories.CreateRegistrationLocationSelector(typeof(TEventEmitter), inner => eventEmitterFactory(inner, BuildTypeInspector())));
return Self;
}
///
/// Registers an additional to be used by the serializer.
///
/// A function that instantiates the event emitter based on a previously registered .
/// Configures the location where to insert the
public StaticSerializerBuilder WithEventEmitter(
WrapperFactory eventEmitterFactory,
Action> where
)
where TEventEmitter : IEventEmitter
{
if (eventEmitterFactory == null)
{
throw new ArgumentNullException(nameof(eventEmitterFactory));
}
if (where == null)
{
throw new ArgumentNullException(nameof(where));
}
where(eventEmitterFactories.CreateTrackingRegistrationLocationSelector(typeof(TEventEmitter), (wrapped, inner) => eventEmitterFactory(wrapped, inner)));
return Self;
}
///
/// Unregisters an existing of type .
///
public StaticSerializerBuilder WithoutEventEmitter()
where TEventEmitter : IEventEmitter
{
return WithoutEventEmitter(typeof(TEventEmitter));
}
///
/// Unregisters an existing of type .
///
public StaticSerializerBuilder WithoutEventEmitter(Type eventEmitterType)
{
if (eventEmitterType == null)
{
throw new ArgumentNullException(nameof(eventEmitterType));
}
eventEmitterFactories.Remove(eventEmitterType);
return this;
}
///
/// Registers a tag mapping.
///
public override StaticSerializerBuilder WithTagMapping(TagName tag, Type type)
{
if (tag.IsEmpty)
{
throw new ArgumentException("Non-specific tags cannot be maped");
}
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (tagMappings.TryGetValue(type, out var alreadyRegisteredTag))
{
throw new ArgumentException($"Type already has a registered tag '{alreadyRegisteredTag}' for type '{type.FullName}'", nameof(type));
}
tagMappings.Add(type, tag);
return this;
}
///
/// Unregisters an existing tag mapping.
///
public StaticSerializerBuilder WithoutTagMapping(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (!tagMappings.Remove(type))
{
throw new KeyNotFoundException($"Tag for type '{type.FullName}' is not registered");
}
return this;
}
///
/// Ensures that it will be possible to deserialize the serialized objects.
/// This option will force the emission of tags and emit only properties with setters.
///
public StaticSerializerBuilder EnsureRoundtrip()
{
objectGraphTraversalStrategyFactory = (typeInspector, typeResolver, typeConverters, maximumRecursion) => new RoundtripObjectGraphTraversalStrategy(
typeConverters,
typeInspector,
typeResolver,
maximumRecursion,
namingConvention,
settings,
factory
);
WithEventEmitter(inner => new TypeAssigningEventEmitter(inner,
tagMappings,
quoteNecessaryStrings,
false,
ScalarStyle.Plain,
YamlFormatter.Default,
enumNamingConvention,
BuildTypeInspector()
), loc => loc.InsteadOf());
return WithTypeInspector(inner => new ReadableAndWritablePropertiesTypeInspector(inner), loc => loc.OnBottom());
}
///
/// Specifies that, if the same object appears more than once in the
/// serialization graph, it will be serialized each time instead of just once.
///
///
/// If the serialization graph contains circular references and this flag is set,
/// a StackOverflowException will be thrown.
/// If this flag is not set, there is a performance penalty because the entire
/// object graph must be walked twice.
///
public StaticSerializerBuilder DisableAliases()
{
preProcessingPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigner));
emissionPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigningObjectGraphVisitor));
return this;
}
///
/// Forces every value to be serialized, even if it is the default value for that type.
///
[Obsolete("The default behavior is now to always emit default values, thefore calling this method has no effect. This behavior is now controlled by ConfigureDefaultValuesHandling.", error: true)]
public StaticSerializerBuilder EmitDefaults() => ConfigureDefaultValuesHandling(DefaultValuesHandling.Preserve);
///
/// Configures how properties with default and null values should be handled. The default value is DefaultValuesHandling.Preserve
///
///
/// If more control is needed, create a class that extends from ChainedObjectGraphVisitor and override its EnterMapping methods.
/// Then register it as follows:
/// WithEmissionPhaseObjectGraphVisitor(args => new MyDefaultHandlingStrategy(args.InnerVisitor));
///
public StaticSerializerBuilder ConfigureDefaultValuesHandling(DefaultValuesHandling configuration)
{
this.defaultValuesHandlingConfiguration = configuration;
return this;
}
///
/// Ensures that the result of the serialization is valid JSON.
///
public StaticSerializerBuilder JsonCompatible()
{
this.emitterSettings = this.emitterSettings
.WithMaxSimpleKeyLength(int.MaxValue)
.WithoutAnchorName();
return this
.WithTypeConverter(new GuidConverter(true), w => w.InsteadOf())
.WithTypeConverter(new DateTime8601Converter(ScalarStyle.DoubleQuoted))
#if NET6_0_OR_GREATER
.WithTypeConverter(new DateOnlyConverter(doubleQuotes: true))
.WithTypeConverter(new TimeOnlyConverter(doubleQuotes: true))
#endif
.WithEventEmitter(inner => new JsonEventEmitter(inner, yamlFormatter, enumNamingConvention, BuildTypeInspector()), loc => loc.InsteadOf());
}
///
/// Allows you to override the new line character to use when serializing to YAML.
///
/// NewLine character(s) to use when serializing to YAML.
public StaticSerializerBuilder WithNewLine(string newLine)
{
this.emitterSettings = this.emitterSettings.WithNewLine(newLine);
return this;
}
///
/// Registers an additional to be used by the serializer
/// before emitting an object graph.
///
///
/// Registering a visitor in the pre-processing phase enables to traverse the object graph once
/// before actually emitting it. This allows a visitor to collect information about the graph that
/// can be used later by another visitor registered in the emission phase.
///
/// The type inspector.
public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor)
where TObjectGraphVisitor : IObjectGraphVisitor
{
return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitor, w => w.OnTop());
}
///
/// Registers an additional to be used by the serializer
/// before emitting an object graph.
///
///
/// Registering a visitor in the pre-processing phase enables to traverse the object graph once
/// before actually emitting it. This allows a visitor to collect information about the graph that
/// can be used later by another visitor registered in the emission phase.
///
/// A function that instantiates the type inspector.
public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(Func, TObjectGraphVisitor> objectGraphVisitorFactory)
where TObjectGraphVisitor : IObjectGraphVisitor
{
return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitorFactory, w => w.OnTop());
}
///
/// Registers an additional to be used by the serializer
/// before emitting an object graph.
///
///
/// Registering a visitor in the pre-processing phase enables to traverse the object graph once
/// before actually emitting it. This allows a visitor to collect information about the graph that
/// can be used later by another visitor registered in the emission phase.
///
/// The type inspector.
/// Configures the location where to insert the
public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(
TObjectGraphVisitor objectGraphVisitor,
Action>> where
)
where TObjectGraphVisitor : IObjectGraphVisitor
{
if (objectGraphVisitor == null)
{
throw new ArgumentNullException(nameof(objectGraphVisitor));
}
if (where == null)
{
throw new ArgumentNullException(nameof(where));
}
where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), _ => objectGraphVisitor));
return this;
}
///
/// Registers an additional to be used by the serializer
/// before emitting an object graph.
///
///
/// Registering a visitor in the pre-processing phase enables to traverse the object graph once
/// before actually emitting it. This allows a visitor to collect information about the graph that
/// can be used later by another visitor registered in the emission phase.
///
/// A function that instantiates the type inspector.
/// Configures the location where to insert the
public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(
Func, TObjectGraphVisitor> objectGraphVisitorFactory,
Action>> where
)
where TObjectGraphVisitor : IObjectGraphVisitor
{
if (objectGraphVisitorFactory == null)
{
throw new ArgumentNullException(nameof(objectGraphVisitorFactory));
}
if (where == null)
{
throw new ArgumentNullException(nameof(where));
}
where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), typeConverters => objectGraphVisitorFactory(typeConverters)));
return this;
}
///
/// Registers an additional to be used by the serializer
/// before emitting an object graph.
///
///
/// Registering a visitor in the pre-processing phase enables to traverse the object graph once
/// before actually emitting it. This allows a visitor to collect information about the graph that
/// can be used later by another visitor registered in the emission phase.
///
/// A function that instantiates the type inspector based on a previously registered .
/// Configures the location where to insert the
public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(
WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory,
Action>> where
)
where TObjectGraphVisitor : IObjectGraphVisitor
{
if (objectGraphVisitorFactory == null)
{
throw new ArgumentNullException(nameof(objectGraphVisitorFactory));
}
if (where == null)
{
throw new ArgumentNullException(nameof(where));
}
where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (wrapped, _) => objectGraphVisitorFactory(wrapped)));
return this;
}
///
/// Registers an additional to be used by the serializer
/// before emitting an object graph.
///
///
/// Registering a visitor in the pre-processing phase enables to traverse the object graph once
/// before actually emitting it. This allows a visitor to collect information about the graph that
/// can be used later by another visitor registered in the emission phase.
///
/// A function that instantiates the type inspector based on a previously registered .
/// Configures the location where to insert the
public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(
WrapperFactory, IObjectGraphVisitor, TObjectGraphVisitor> objectGraphVisitorFactory,
Action>> where
)
where TObjectGraphVisitor : IObjectGraphVisitor
{
if (objectGraphVisitorFactory == null)
{
throw new ArgumentNullException(nameof(objectGraphVisitorFactory));
}
if (where == null)
{
throw new ArgumentNullException(nameof(where));
}
where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (wrapped, typeConverters) => objectGraphVisitorFactory(wrapped, typeConverters)));
return this;
}
///
/// Unregisters an existing of type .
///
public StaticSerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor()
where TObjectGraphVisitor : IObjectGraphVisitor
{
return WithoutPreProcessingPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor));
}
///
/// Unregisters an existing of type .
///
public StaticSerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor(Type objectGraphVisitorType)
{
if (objectGraphVisitorType == null)
{
throw new ArgumentNullException(nameof(objectGraphVisitorType));
}
preProcessingPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType);
return this;
}
///
/// Registers an to be used by the serializer
/// while traversing the object graph.
///
/// A function that instantiates the traversal strategy.
public StaticSerializerBuilder WithObjectGraphTraversalStrategyFactory(ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory)
{
this.objectGraphTraversalStrategyFactory = objectGraphTraversalStrategyFactory;
return this;
}
///
/// Registers an additional to be used by the serializer
/// while emitting an object graph.
///
/// A function that instantiates the type inspector.
public StaticSerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory)
where TObjectGraphVisitor : IObjectGraphVisitor
{
return WithEmissionPhaseObjectGraphVisitor(objectGraphVisitorFactory, w => w.OnTop());
}
///
/// Registers an additional to be used by the serializer
/// while emitting an object graph.
///
/// A function that instantiates the type inspector.
/// Configures the location where to insert the
public StaticSerializerBuilder WithEmissionPhaseObjectGraphVisitor(
Func objectGraphVisitorFactory,
Action>> where
)
where TObjectGraphVisitor : IObjectGraphVisitor
{
if (objectGraphVisitorFactory == null)
{
throw new ArgumentNullException(nameof(objectGraphVisitorFactory));
}
if (where == null)
{
throw new ArgumentNullException(nameof(where));
}
where(emissionPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), args => objectGraphVisitorFactory(args)));
return this;
}
///
/// Registers an additional to be used by the serializer
/// while emitting an object graph.
///
/// A function that instantiates the type inspector based on a previously registered .
/// Configures the location where to insert the
public StaticSerializerBuilder WithEmissionPhaseObjectGraphVisitor(
WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory,
Action>> where
)
where TObjectGraphVisitor : IObjectGraphVisitor
{
if (objectGraphVisitorFactory == null)
{
throw new ArgumentNullException(nameof(objectGraphVisitorFactory));
}
if (where == null)
{
throw new ArgumentNullException(nameof(where));
}
where(emissionPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (wrapped, args) => objectGraphVisitorFactory(wrapped, args)));
return this;
}
///
/// Unregisters an existing of type .
///
public StaticSerializerBuilder WithoutEmissionPhaseObjectGraphVisitor()
where TObjectGraphVisitor : IObjectGraphVisitor
{
return WithoutEmissionPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor));
}
///
/// Unregisters an existing of type .
///
public StaticSerializerBuilder WithoutEmissionPhaseObjectGraphVisitor(Type objectGraphVisitorType)
{
if (objectGraphVisitorType == null)
{
throw new ArgumentNullException(nameof(objectGraphVisitorType));
}
emissionPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType);
return this;
}
///
/// Creates sequences with extra indentation
///
///
/// list:
/// - item
/// - item
///
///
public StaticSerializerBuilder WithIndentedSequences()
{
emitterSettings = emitterSettings.WithIndentedSequences();
return this;
}
///
/// Creates a new according to the current configuration.
///
public ISerializer Build()
{
return Serializer.FromValueSerializer(BuildValueSerializer(), emitterSettings);
}
///
/// Creates a new that implements the current configuration.
/// This method is available for advanced scenarios. The preferred way to customize the behavior of the
/// deserializer is to use the method.
///
public IValueSerializer BuildValueSerializer()
{
var typeConverters = BuildTypeConverters();
var typeInspector = BuildTypeInspector();
var traversalStrategy = objectGraphTraversalStrategyFactory(typeInspector, typeResolver, typeConverters, maximumRecursion);
var eventEmitter = eventEmitterFactories.BuildComponentChain(new WriterEventEmitter());
return new ValueSerializer(
traversalStrategy,
eventEmitter,
typeConverters,
preProcessingPhaseObjectGraphVisitorFactories.Clone(),
emissionPhaseObjectGraphVisitorFactories.Clone()
);
}
///
/// Builds the type inspector used by various classes to get information about types and their members.
///
///
public ITypeInspector BuildTypeInspector()
{
var typeInspector = context.GetTypeInspector();
return typeInspectorFactories.BuildComponentChain(typeInspector);
}
private class ValueSerializer : IValueSerializer
{
private readonly IObjectGraphTraversalStrategy traversalStrategy;
private readonly IEventEmitter eventEmitter;
private readonly IEnumerable typeConverters;
private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories;
private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories;
public ValueSerializer(
IObjectGraphTraversalStrategy traversalStrategy,
IEventEmitter eventEmitter,
IEnumerable typeConverters,
LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories,
LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories
)
{
this.traversalStrategy = traversalStrategy;
this.eventEmitter = eventEmitter;
this.typeConverters = typeConverters;
this.preProcessingPhaseObjectGraphVisitorFactories = preProcessingPhaseObjectGraphVisitorFactories;
this.emissionPhaseObjectGraphVisitorFactories = emissionPhaseObjectGraphVisitorFactories;
}
public void SerializeValue(IEmitter emitter, object? value, Type? type)
{
var actualType = type ?? (value != null ? value.GetType() : typeof(object));
var staticType = type ?? typeof(object);
var graph = new ObjectDescriptor(value, actualType, staticType);
void NestedObjectSerializer(object? v, Type? t)
{
SerializeValue(emitter, v, t);
}
var preProcessingPhaseObjectGraphVisitors = preProcessingPhaseObjectGraphVisitorFactories.BuildComponentList(typeConverters);
foreach (var visitor in preProcessingPhaseObjectGraphVisitors)
{
traversalStrategy.Traverse(graph, visitor, default, NestedObjectSerializer);
}
var emittingVisitor = emissionPhaseObjectGraphVisitorFactories.BuildComponentChain(
new EmittingObjectGraphVisitor(eventEmitter),
inner => new EmissionPhaseObjectGraphVisitorArgs(inner, eventEmitter, preProcessingPhaseObjectGraphVisitors, typeConverters, NestedObjectSerializer)
);
traversalStrategy.Traverse(graph, emittingVisitor, emitter, NestedObjectSerializer);
}
}
}
}