# Serializer settings Verify uses [Argon](https://github.com/SimonCropp/Argon) for serialization. See [Default Settings](#default-settings) for on how Argon is used and instructions on how to control that usage. Serialization settings can be customized at three levels: * Method: Will run the verification in the current test method. * Class: Will run for all verifications in all test methods for a test class. * Global: Will run for test methods on all tests. ## Not valid json Note that the output is technically not valid json. * Names and values are not quoted. * Newlines are not escaped. The reason for these is that it makes approval files cleaner and easier to read and visualize/understand differences. ### UseStrictJson To use strict json call `VerifierSettings.UseStrictJson`: #### Globally ```cs [ModuleInitializer] public static void Init() => VerifierSettings.UseStrictJson(); ``` snippet source | anchor #### Instance ```cs var target = new TheTarget { Value = "Foo" }; var settings = new VerifySettings(); settings.UseStrictJson(); await Verify(target, settings); ``` snippet source | anchor #### Fluent ```cs var target = new TheTarget { Value = "Foo" }; await Verify(target) .UseStrictJson(); ``` snippet source | anchor #### Result Then this results in * The default `.received.` and `.verified.` extensions for serialized verification to be `.json`. * `JsonTextWriter.QuoteChar` to be `"`. * `JsonTextWriter.QuoteName` to be `true`. Then when an object is verified: ```cs var target = new TheTarget { Value = "Foo" }; await Verify(target); ``` snippet source | anchor The resulting file will be: ```json { "Value": "Foo" } ``` snippet source | anchor ## Default settings Verify uses [Argon](https://github.com/SimonCropp/Argon) for serialization. > Argon is a JSON framework for .NET. It is a hard fork of Newtonsoft.Json. See [Argon documentation](https://github.com/SimonCropp/Argon/blob/main/docs/readme.md) The default `JsonSerializerSettings` are: ```cs var settings = new JsonSerializerSettings { Formatting = Formatting.Indented, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore }; ``` snippet source | anchor ### Modify Defaults #### Globally ```cs VerifierSettings .AddExtraSettings(_ => _.TypeNameHandling = TypeNameHandling.All); ``` snippet source | anchor #### Instance ```cs [Fact] public Task AddExtraSettings() { var settings = new VerifySettings(); settings .AddExtraSettings( _ => _.SerializeError = (currentObject, originalObject, location, member, exception, handled) => Console.WriteLine(member)); return Verify("Value", settings); } ``` snippet source | anchor #### Fluent ```cs [Fact] public Task AddExtraSettingsFluent() => Verify("Value") .AddExtraSettings( _ => _.SerializeError = (currentObject, originalObject, location, member, exception, handled) => Console.WriteLine(member)); ``` snippet source | anchor ## QuoteName is false [JsonTextWriter.QuoteName](https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonTextWriter_QuoteName.htm) is set to false. The reason for this is that it makes approval files cleaner and easier to read and visualize/understand differences. ## Empty collections are ignored By default empty collections are ignored during verification. To disable this behavior globally use: ```cs VerifierSettings.DontIgnoreEmptyCollections(); ``` snippet source | anchor ## Changing Argon settings Extra Argon settings can be made: ### Globally ```cs VerifierSettings.AddExtraSettings( _ => _.TypeNameHandling = TypeNameHandling.All); ``` snippet source | anchor ### Instance ```cs var settings = new VerifySettings(); settings.AddExtraSettings( _ => _.TypeNameHandling = TypeNameHandling.All); ``` snippet source | anchor ### Argon Converter One common use case is to register a custom [JsonConverter](https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm). As only writing is required, to help with this there is `WriteOnlyJsonConverter`, and `WriteOnlyJsonConverter`. For example given the following JsonConverter: ```cs class CompanyConverter : WriteOnlyJsonConverter { public override void Write(VerifyJsonWriter writer, Company company) { writer.WriteMember(company, company.Name, "Name"); writer.WriteMember(company, company.Employees, "Employees"); } } ``` snippet source | anchor It can be added in a ModuleInitializer: ```cs [ModuleInitializer] public static void Initialize() => VerifierSettings.AddExtraSettings(_ => _.Converters.Add(new CompanyConverter())); ``` snippet source | anchor #### VerifyJsonWriter A `VerifyJsonWriter` is passed in to the `Write` methods. It exposes context and helper methods to the JsonConverter. For example: * `Counter` property that gives programmatic access to the counting behavior used by [Guid](guids.md), [Date](dates.md), and [Id](#numeric-ids-are-scrubbed) scrubbing. * `Serializer` property that exposes the current `JsonSerializer`. * `Serialize(object value)` is a convenience method that calls `JsonSerializer.Serialize` passing in the writer instance and the `value` parameter. * `WriteProperty(T target, TMember value, string name)` method that writes a property name and value while respecting other custom serialization settings eg [member converters](#converting-a-member), [ignore rules](#ignoring-a-type) etc. #### Testing JsonConverters `WriteOnlyJsonConverter` has a `Execute` methods that executes a JsonConverter: ```cs namespace VerifyTests; public abstract partial class WriteOnlyJsonConverter { public static string Execute( object target, VerifySettings? settings = null) where TConverter : WriteOnlyJsonConverter, new() => Execute(new TConverter(), target, settings); public static string Execute(TConverter converter, object target, VerifySettings? settings = null) where TConverter : WriteOnlyJsonConverter { settings ??= new(); settings.UseStrictJson(); var builder = new StringBuilder("{"); using var counter = Counter.Start(); using (var writer = new VerifyJsonWriter(builder, settings, counter)) { converter.Write(writer, target); } builder.Append('}'); return builder.ToString(); } } ``` snippet source | anchor This can be used to test a JsonConverter. Given the following JsonConverter: ```cs class CompanyConverter : WriteOnlyJsonConverter { public override void Write(VerifyJsonWriter writer, Company company) { writer.WriteMember(company, company.Name, "Name"); writer.WriteMember(company, company.Employees, "Employees"); } } ``` snippet source | anchor It can be tested with: ```cs [Fact] public Task Test() { var company = new Company( "Company Name", Employees: [ "Employee1", "Employee2" ]); var result = WriteOnlyJsonConverter.Execute(company); return VerifyJson(result); } ``` snippet source | anchor Json converters often have instance level configuration or contextual settings. ```cs class CompanyConverter : WriteOnlyJsonConverter { bool ignoreEmployees; public CompanyConverter() { } public CompanyConverter(bool ignoreEmployees) => this.ignoreEmployees = ignoreEmployees; public override void Write(VerifyJsonWriter writer, Company company) { writer.WriteMember(company, company.Name, "Name"); if (!ignoreEmployees) { if (writer.Context.TryGetValue("IgnoreCompanyEmployees", out var value)) { if (value is true) { return; } } writer.WriteMember(company, company.Employees, "Employees"); } } } ``` snippet source | anchor ```cs public static class CompanyConverterSettings { public static void IgnoreCompanyEmployees(this VerifySettings settings) => settings.Context["IgnoreCompanyEmployees"] = true; public static SettingsTask IgnoreCompanyEmployees(this SettingsTask settings) { settings.CurrentSettings.IgnoreCompanyEmployees(); return settings; } } ``` snippet source | anchor These can be tested: ```cs [Fact] public Task TestWithConverterInstance() { var company = new Company( "Company Name", Employees: [ "Employee1", "Employee2" ]); var converter = new CompanyConverter(ignoreEmployees: true); var result = WriteOnlyJsonConverter.Execute(converter, company); return VerifyJson(result); } ``` snippet source | anchor ```cs [Fact] public Task TestWithSettings() { var company = new Company( "Company Name", Employees: [ "Employee1", "Employee2" ]); var settings = new VerifySettings(); settings.IgnoreCompanyEmployees(); var result = WriteOnlyJsonConverter.Execute(company, settings); return VerifyJson(result); } ``` snippet source | anchor ## Scoped settings ```cs [Fact] public Task ScopedSerializer() { var person = new Person { GivenNames = "John", FamilyName = "Smith" }; var settings = new VerifySettings(); settings.AddExtraSettings(_ => _.TypeNameHandling = TypeNameHandling.All); return Verify(person, settings); } [Fact] public Task ScopedSerializerFluent() { var person = new Person { GivenNames = "John", FamilyName = "Smith" }; return Verify(person) .AddExtraSettings(_ => _.TypeNameHandling = TypeNameHandling.All); } ``` snippet source | anchor Result: ```txt { $type: VerifyObjectSamples.Person, GivenNames: John, FamilyName: Smith } ``` snippet source | anchor ## Ignoring a type To ignore all members that match a certain type: ```cs [Fact] public Task IgnoreType() { var target = new IgnoreTypeTarget { ToIgnore = new() { Property = "Value" }, ToIgnoreNullable = new() { Property = "Value" }, ToIgnoreByInterface = new() { Property = "Value" }, ToIgnoreByBase = new() { Property = "Value" }, ToIgnoreByBaseGeneric = new() { Property = "Value" }, ToIgnoreByType = new() { Property = "Value" }, ToInclude = new() { Property = "Value" }, ToIncludeNullable = new() { Property = "Value" }, ToIgnoreStruct = new("Value"), ToIgnoreStructNullable = new("Value"), ToIncludeStruct = new("Value"), ToIncludeStructNullable = new("Value") }; var settings = new VerifySettings(); settings.IgnoreMembersWithType(); settings.IgnoreMembersWithType(); settings.IgnoreMembersWithType(); settings.IgnoreMembersWithType(); settings.IgnoreMembersWithType(typeof(BaseToIgnoreGeneric<>)); settings.IgnoreMembersWithType(); return Verify(target, settings); } [Fact] public Task IgnoreTypeFluent() { var target = new IgnoreTypeTarget { ToIgnore = new() { Property = "Value" }, ToIgnoreNullable = new() { Property = "Value" }, ToIgnoreByInterface = new() { Property = "Value" }, ToIgnoreByBase = new() { Property = "Value" }, ToIgnoreByBaseGeneric = new() { Property = "Value" }, ToIgnoreByType = new() { Property = "Value" }, ToInclude = new() { Property = "Value" }, ToIncludeNullable = new() { Property = "Value" }, ToIgnoreStruct = new("Value"), ToIgnoreStructNullable = new("Value"), ToIncludeStruct = new("Value"), ToIncludeStructNullable = new("Value") }; return Verify(target) .IgnoreMembersWithType() .IgnoreMembersWithType() .IgnoreMembersWithType() .IgnoreMembersWithType() .IgnoreMembersWithType(typeof(BaseToIgnoreGeneric<>)) .IgnoreMembersWithType(); } ``` snippet source | anchor Or globally: ```cs VerifierSettings.IgnoreMembersWithType(); ``` snippet source | anchor Result: ```txt { ToInclude: { Property: Value }, ToIncludeNullable: { Property: Value }, ToIncludeStruct: { Property: Value }, ToIncludeStructNullable: { Property: Value } } ``` snippet source | anchor ## Scrub a type To scrub all members that match a certain type: ```cs [Fact] public Task ScrubType() { var target = new IgnoreTypeTarget { ToIgnore = new() { Property = "Value" }, ToIgnoreNullable = new() { Property = "Value" }, ToIgnoreByInterface = new() { Property = "Value" }, ToIgnoreByBase = new() { Property = "Value" }, ToIgnoreByBaseGeneric = new() { Property = "Value" }, ToIgnoreByType = new() { Property = "Value" }, ToInclude = new() { Property = "Value" }, ToIncludeNullable = new() { Property = "Value" }, ToIgnoreStruct = new("Value"), ToIgnoreStructNullable = new("Value"), ToIncludeStruct = new("Value"), ToIncludeStructNullable = new("Value") }; var settings = new VerifySettings(); settings.ScrubMembersWithType(); settings.ScrubMembersWithType(); settings.ScrubMembersWithType(); settings.ScrubMembersWithType(); settings.ScrubMembersWithType(typeof(BaseToIgnoreGeneric<>)); settings.ScrubMembersWithType(); return Verify(target, settings); } [Fact] public Task ScrubTypeFluent() { var target = new IgnoreTypeTarget { ToIgnore = new() { Property = "Value" }, ToIgnoreNullable = new() { Property = "Value" }, ToIgnoreByInterface = new() { Property = "Value" }, ToIgnoreByBase = new() { Property = "Value" }, ToIgnoreByBaseGeneric = new() { Property = "Value" }, ToIgnoreByType = new() { Property = "Value" }, ToInclude = new() { Property = "Value" }, ToIncludeNullable = new() { Property = "Value" }, ToIgnoreStruct = new("Value"), ToIgnoreStructNullable = new("Value"), ToIncludeStruct = new("Value"), ToIncludeStructNullable = new("Value") }; return Verify(target) .ScrubMembersWithType() .ScrubMembersWithType() .ScrubMembersWithType() .ScrubMembersWithType() .ScrubMembersWithType(typeof(BaseToIgnoreGeneric<>)) .ScrubMembersWithType(); } ``` snippet source | anchor Or globally: ```cs VerifierSettings.ScrubMembersWithType(); ``` snippet source | anchor Result: ```txt { ToIgnore: {Scrubbed}, ToIgnoreByType: {Scrubbed}, ToIgnoreByInterface: {Scrubbed}, ToIgnoreByBase: {Scrubbed}, ToIgnoreByBaseGeneric: {Scrubbed}, ToIgnoreNullable: {Scrubbed}, ToIgnoreStruct: {Scrubbed}, ToIgnoreStructNullable: {Scrubbed}, ToInclude: { Property: Value }, ToIncludeNullable: { Property: Value }, ToIncludeStruct: { Property: Value }, ToIncludeStructNullable: { Property: Value } } ``` snippet source | anchor ## Ignoring an instance To ignore instances of a type based on delegate: ```cs [Fact] public Task AddIgnoreInstance() { var target = new IgnoreInstanceTarget { ToIgnore = new() { Property = "Ignore" }, ToInclude = new() { Property = "Include" } }; var settings = new VerifySettings(); settings.IgnoreInstance(_ => _.Property == "Ignore"); return Verify(target, settings); } [Fact] public Task AddIgnoreInstanceFluent() { var target = new IgnoreInstanceTarget { ToIgnore = new() { Property = "Ignore" }, ToInclude = new() { Property = "Include" } }; return Verify(target) .IgnoreInstance(_ => _.Property == "Ignore"); } ``` snippet source | anchor Or globally: ```cs VerifierSettings.IgnoreInstance(_ => _.Property == "Ignore"); ``` snippet source | anchor Result: ```txt { ToInclude: { Property: Include } } ``` snippet source | anchor ## Scrub a instance To scrub instances of a type based on delegate: ```cs [Fact] public Task AddScrubInstance() { var target = new IgnoreInstanceTarget { ToIgnore = new() { Property = "Ignore" }, ToInclude = new() { Property = "Include" } }; var settings = new VerifySettings(); settings.ScrubInstance(_ => _.Property == "Ignore"); return Verify(target, settings); } [Fact] public Task AddScrubInstanceFluent() { var target = new IgnoreInstanceTarget { ToIgnore = new() { Property = "Ignore" }, ToInclude = new() { Property = "Include" } }; return Verify(target) .ScrubInstance(_ => _.Property == "Ignore"); } ``` snippet source | anchor Or globally: ```cs VerifierSettings.ScrubInstance(_ => _.Property == "Ignore"); ``` snippet source | anchor Result: ```txt { ToIgnore: {Scrubbed}, ToInclude: { Property: Include } } ``` snippet source | anchor ## Ignore member by expressions To ignore members of a certain type using an expression: ```cs [Fact] public Task IgnoreMemberByExpression() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyWithPropertyName = "Value" }; var settings = new VerifySettings(); settings.IgnoreMembers( _ => _.Property, _ => _.PropertyWithPropertyName, _ => _.Field, _ => _.GetOnlyProperty, _ => _.PropertyThatThrows); return Verify(target, settings); } [Fact] public Task IgnoreMemberByExpressionFluent() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value" }; return Verify(target) .IgnoreMembers( _ => _.Property, _ => _.Field, _ => _.GetOnlyProperty, _ => _.PropertyThatThrows); } ``` snippet source | anchor Or globally ```cs VerifierSettings.IgnoreMembers( _ => _.Property, _ => _.PropertyWithPropertyName, _ => _.Field, _ => _.GetOnlyProperty, _ => _.PropertyThatThrows); ``` snippet source | anchor Result: ```txt { Include: Value } ``` snippet source | anchor ## Scrub member by expressions To scrub members of a certain type using an expression: ```cs [Fact] public Task ScrubMemberByExpression() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyWithPropertyName = "Value" }; var settings = new VerifySettings(); settings.ScrubMembers( _ => _.Property, _ => _.PropertyWithPropertyName, _ => _.Field, _ => _.GetOnlyProperty, _ => _.PropertyThatThrows); return Verify(target, settings); } [Fact] public Task ScrubMemberByExpressionFluent() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value" }; return Verify(target) .ScrubMembers( _ => _.Property, _ => _.Field, _ => _.GetOnlyProperty, _ => _.PropertyThatThrows); } ``` snippet source | anchor Or globally ```cs VerifierSettings.ScrubMembers( _ => _.Property, _ => _.PropertyWithPropertyName, _ => _.Field, _ => _.GetOnlyProperty, _ => _.PropertyThatThrows); ``` snippet source | anchor Result: ```txt { Field: {Scrubbed}, Include: Value, Property: {Scrubbed}, _Custom: {Scrubbed}, GetOnlyProperty: {Scrubbed}, PropertyThatThrows: {Scrubbed} } ``` snippet source | anchor ## Ignore member by name To ignore members of a certain type using type and name: ```cs [Fact] public Task IgnoreMemberByName() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyByName = "Value" }; var settings = new VerifySettings(); // For all types settings.IgnoreMember("PropertyByName"); // For a specific type settings.IgnoreMember(typeof(IgnoreExplicitTarget), "Property"); // For a specific type generic settings.IgnoreMember("Field"); // For a specific type with expression settings.IgnoreMember(_ => _.PropertyThatThrows); return Verify(target, settings); } [Fact] public Task IgnoreMemberByNameFluent() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyByName = "Value" }; return Verify(target) // For all types .IgnoreMember("PropertyByName") // For a specific type .IgnoreMember(typeof(IgnoreExplicitTarget), "Property") // For a specific type generic .IgnoreMember("Field") // For a specific type with expression .IgnoreMember(_ => _.PropertyThatThrows); } ``` snippet source | anchor Or globally: ```cs // For all types VerifierSettings.IgnoreMember("PropertyByName"); // For a specific type VerifierSettings.IgnoreMember(typeof(IgnoreExplicitTarget), "Property"); // For a specific type generic VerifierSettings.IgnoreMember("Field"); // For a specific type with expression VerifierSettings.IgnoreMember(_ => _.PropertyThatThrows); ``` snippet source | anchor Result: ```txt { Include: Value, GetOnlyProperty: asd } ``` snippet source | anchor ## Scrub member by name To scrub members of a certain type using type and name: ```cs [Fact] public Task ScrubMemberByName() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyByName = "Value" }; var settings = new VerifySettings(); // For all types settings.ScrubMember("PropertyByName"); // For a specific type settings.ScrubMember(typeof(IgnoreExplicitTarget), "Property"); // For a specific type generic settings.ScrubMember("Field"); // For a specific type with expression settings.ScrubMember(_ => _.PropertyThatThrows); return Verify(target, settings); } [Fact] public Task ScrubMemberByNameFluent() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyByName = "Value" }; return Verify(target) // For all types .ScrubMember("PropertyByName") // For a specific type .ScrubMember(typeof(IgnoreExplicitTarget), "Property") // For a specific type generic .ScrubMember("Field") // For a specific type with expression .ScrubMember(_ => _.PropertyThatThrows); } ``` snippet source | anchor Or globally: ```cs // For all types VerifierSettings.ScrubMember("PropertyByName"); // For a specific type VerifierSettings.ScrubMember(typeof(IgnoreExplicitTarget), "Property"); // For a specific type generic VerifierSettings.ScrubMember("Field"); // For a specific type with expression VerifierSettings.ScrubMember(_ => _.PropertyThatThrows); ``` snippet source | anchor Result: ```txt { Field: {Scrubbed}, Include: Value, Property: {Scrubbed}, PropertyByName: {Scrubbed}, GetOnlyProperty: asd, PropertyThatThrows: {Scrubbed} } ``` snippet source | anchor ## Ignore member by predicate To ignore members of a certain type using a predicate function: ```cs [Fact] public Task IgnoreMemberByPredicate() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyByName = "Value" }; var settings = new VerifySettings(); settings.IgnoreMembers(_ => _ is "Field" or "Property"); settings.IgnoreMembers(_ => _.Name is "PropertyByName" or "PropertyThatThrows"); return Verify(target, settings); } [Fact] public Task IgnoreMemberByPredicateFluent() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyByName = "Value" }; var settings = new VerifySettings(); return Verify(target, settings) .IgnoreMembers(_ => _ is "Field" or "Property") .IgnoreMembers(_ => _.Name is "PropertyByName" or "PropertyThatThrows"); } [Fact] public Task IgnoreDictionaryByPredicate() { var settings = new VerifySettings(); settings.IgnoreMembers(name => name is "Ignore"); var target = new Dictionary { { "Include", new Dictionary { { "Ignore", "Value1" }, { "Key1", "Value2" } } }, { "Ignore", "Value3" }, { "Key2", "Value4" } }; return Verify(target, settings); } ``` snippet source | anchor Or globally: ```cs VerifierSettings.IgnoreMembers( _=>_.DeclaringType == typeof(TargetClass) && _.Name == "Proprty"); ``` snippet source | anchor Result: ```txt { Include: Value, GetOnlyProperty: asd } ``` snippet source | anchor ## Scrub member by predicate To scrub members of a certain type using a predicate function: ```cs [Fact] public Task ScrubMemberByPredicate() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyByName = "Value" }; var settings = new VerifySettings(); settings.ScrubMembers(_ => _ is "Field" or "Property"); settings.ScrubMembers(_ => _.Name is "PropertyByName" or "PropertyThatThrows"); return Verify(target, settings); } [Fact] public Task ScrubMemberByPredicateFluent() { var target = new IgnoreExplicitTarget { Include = "Value", Field = "Value", Property = "Value", PropertyByName = "Value" }; var settings = new VerifySettings(); return Verify(target, settings) .ScrubMembers(name => name is "Field" or "Property") .ScrubMembers(member => member.Name is "PropertyByName" or "PropertyThatThrows"); } [Fact] public Task ScrubDictionaryByPredicate() { var settings = new VerifySettings(); settings.ScrubMembers(name => name is "Ignore"); var target = new Dictionary { { "Include", new Dictionary { { "Ignore", "Value1" }, { "Key1", "Value2" } } }, { "Ignore", "Value3" }, { "Key2", "Value4" } }; return Verify(target, settings); } ``` snippet source | anchor Or globally: ```cs VerifierSettings.ScrubMembers( _=>_.DeclaringType == typeof(TargetClass) && _.Name == "Proprty"); ``` snippet source | anchor Result: ```txt { Field: {Scrubbed}, Include: Value, Property: {Scrubbed}, PropertyByName: {Scrubbed}, GetOnlyProperty: asd, PropertyThatThrows: {Scrubbed} } ``` snippet source | anchor ## Converting a member The value of a member can be mutated before serialization: ```cs [ModuleInitializer] public static void MemberConverterByExpressionInit() { // using only the member VerifierSettings.MemberConverter( expression: _ => _.Field, converter: _ => $"{_}_Suffix"); // using target and member VerifierSettings.MemberConverter( expression: _ => _.Property, converter: (target, member) => $"{target}_{member}_Suffix"); } [Fact] public Task MemberConverterByExpression() { var input = new MemberTarget { Field = "FieldValue", Property = "PropertyValue" }; return Verify(input); } ``` snippet source | anchor ## See also * [Obsolete members](/docs/obsolete-members.md) * [Type to string mapping](/docs/type-to-string-mapping.md) * [Guids](/docs/guids.md) * [Dates](/docs/dates.md) * [Scrubbing](/docs/scrubbers.md) * [Members that throw](/docs/members-throw.md) * [Ordering](/docs/ordering.md) * [Encoding](/docs/encoding.md) * [JsonAppender](/docs/jsonappender.md)