Advertisement
pszczyg

ConverterResolverPerformanceComparison

Apr 8th, 2024 (edited)
488
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.52 KB | None | 0 0
  1. using System;
  2. using System.Linq;
  3. using System.Reflection;
  4. using Newtonsoft.Json;
  5. using BenchmarkDotNet.Attributes;
  6. using BenchmarkDotNet.Running;
  7. using Newtonsoft.Json.Linq;
  8. using Newtonsoft.Json.Serialization;
  9.  
  10. namespace ConsoleApp
  11. {
  12.     internal class Program
  13.     {
  14.         static void Main()
  15.         {
  16.             BenchmarkRunner.Run<ListBenchmarks>();
  17.         }
  18.     }
  19.  
  20.     //Attribute used to decorate properties containing sensitive data
  21.     [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
  22.     public class SensitiveDataAttribute : Attribute
  23.     {
  24.         public SensitiveDataAttribute(string mask = "***")
  25.         {
  26.             Mask = mask;
  27.         }
  28.         public string Mask { get; } //Adjustable mask
  29.     }
  30.  
  31.     public class ClassWithSensitiveData : IContainSecretData
  32.     {
  33.         [JsonIgnore]
  34.         public string Header { get; set; } //This will not be serialized at all
  35.         public string UserName { get; set; }
  36.         [SensitiveData]
  37.         public string Password { get; set; } //This will be serialized in form "Password=***"
  38.         public SubClassWithSensitiveData SensitiveData { get; set; }
  39.     }
  40.  
  41.     public class SubClassWithSensitiveData : IContainSecretData
  42.     {
  43.         [SensitiveData]
  44.         public string Header { get; set; } //Nested data we want to protect
  45.         public string NotSecret { get; set; }
  46.     }
  47.  
  48.     public interface IContainSecretData { } //Marker interface to recognize the objects containing sensitive data
  49.  
  50.     public class MaskSensitiveDataConverter : JsonConverter
  51.     {
  52.         public override bool CanConvert(Type objectType)
  53.         {
  54.             return typeof(IContainSecretData).IsAssignableFrom(objectType);
  55.         }
  56.  
  57.         public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  58.         {
  59.             var jObject = new JObject();
  60.             foreach (var property in value.GetType().GetProperties())
  61.             {
  62.                 var attributes = property.GetCustomAttributes(true);
  63.                 if (attributes.Any(attr => attr is JsonIgnoreAttribute)) { continue; }
  64.                 var secretAttribute = attributes.OfType<SensitiveDataAttribute>().FirstOrDefault();
  65.                 if (secretAttribute != null)
  66.                 {
  67.                     jObject.Add(property.Name, secretAttribute.Mask);
  68.                 }
  69.                 else
  70.                 {
  71.                     var propValue = property.GetValue(value);
  72.                     if (propValue != null || serializer.NullValueHandling == NullValueHandling.Include)
  73.                     {
  74.                         var propToken = propValue != null ? JToken.FromObject(propValue, serializer) : JValue.CreateNull();
  75.                         jObject.Add(property.Name, propToken);
  76.                     }
  77.                 }
  78.             }
  79.             jObject.WriteTo(writer);
  80.         }
  81.  
  82.         public override bool CanRead => false;
  83.         public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException("CanRead is false, this method cannot be invoked.");
  84.     }
  85.  
  86.     public class MaskSensitiveDataProvider : IValueProvider
  87.     {
  88.         const string _sesitiveDatatag = "***";
  89.         public object GetValue(object target)
  90.         {
  91.             return _sesitiveDatatag;
  92.         }
  93.  
  94.         public void SetValue(object target, object value)
  95.         {
  96.             target = _sesitiveDatatag;
  97.         }
  98.     }
  99.  
  100.     public class MaskSensitiveDataResolver : DefaultContractResolver
  101.     {
  102.         protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
  103.         {
  104.             var property = base.CreateProperty(member, memberSerialization);
  105.  
  106.             if (member is PropertyInfo)
  107.             {
  108.                 var prop = (PropertyInfo)member;
  109.                 var isSensitiveData = Attribute.IsDefined(prop, typeof(SensitiveDataAttribute));
  110.  
  111.                 if (isSensitiveData)
  112.                 {
  113.                     property.ValueProvider = new MaskSensitiveDataProvider();
  114.                 }
  115.             }
  116.  
  117.             return property;
  118.         }
  119.     }
  120.  
  121.     public class ListBenchmarks
  122.     {
  123.         //Base test to compare results with. It just serializes the data without any modifications
  124.         [Benchmark(Baseline = true)]
  125.         public string WithoutConverter()
  126.         {
  127.             var data = new ClassWithSensitiveData
  128.             {
  129.                 Password = "password",
  130.                 SensitiveData = new SubClassWithSensitiveData
  131.                 {
  132.                     Header = "HEADER",
  133.                     NotSecret = "NotSecret"
  134.                 }
  135.             };
  136.             return JsonConvert.SerializeObject(data, Formatting.None, new JsonSerializerSettings
  137.             {
  138.                 NullValueHandling = NullValueHandling.Ignore,
  139.                 ReferenceLoopHandling = ReferenceLoopHandling.Ignore
  140.             });
  141.         }
  142.         //Test that serializes using converter
  143.         [Benchmark]
  144.         public string WithConverter()
  145.         {
  146.             var data = new ClassWithSensitiveData
  147.             {
  148.                 Password = "password",
  149.                 SensitiveData = new SubClassWithSensitiveData
  150.                 {
  151.                     Header = "HEADER",
  152.                     NotSecret = "NotSecret"
  153.                 }
  154.             };
  155.             return JsonConvert.SerializeObject(data, Formatting.None, new JsonSerializerSettings
  156.             {
  157.                 NullValueHandling = NullValueHandling.Ignore,
  158.                 ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
  159.                 Converters = new[] { new MaskSensitiveDataConverter() }
  160.             });
  161.         }
  162.         //And the one that serializes using resolver
  163.         [Benchmark]
  164.         public string WithDataResolver()
  165.         {
  166.             var data = new ClassWithSensitiveData
  167.             {
  168.                 Password = "password",
  169.                 SensitiveData = new SubClassWithSensitiveData
  170.                 {
  171.                     Header = "HEADER",
  172.                     NotSecret = "NotSecret"
  173.                 }
  174.             };
  175.             return JsonConvert.SerializeObject(data, Formatting.None, new JsonSerializerSettings
  176.             {
  177.                 NullValueHandling = NullValueHandling.Ignore,
  178.                 ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
  179.                 ContractResolver = new MaskSensitiveDataResolver()
  180.             });
  181.         }
  182.     }
  183. }
  184.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement