From 317eebe789e20e099e6fc55879327cc1109f79a3 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 23 Jan 2022 11:41:00 +0100 Subject: [PATCH] Fixed message validation failing on macros Benchmarked JSON formatting, changed implementation accordingly --- .../PettingZoo.Benchmark.csproj | 15 + PettingZoo.Benchmark/Program.cs | 138 ++++++++ .../Rendering/MessageBodyRenderer.cs | 16 +- .../PettingZoo.Tapeti_ooj5vuwa_wpftmp.csproj | 325 ------------------ PettingZoo.sln | 8 +- .../Publisher/PayloadEditorControl.xaml.cs | 11 +- .../Tab/Publisher/PayloadEditorViewModel.cs | 13 +- 7 files changed, 188 insertions(+), 338 deletions(-) create mode 100644 PettingZoo.Benchmark/PettingZoo.Benchmark.csproj create mode 100644 PettingZoo.Benchmark/Program.cs delete mode 100644 PettingZoo.Tapeti/PettingZoo.Tapeti_ooj5vuwa_wpftmp.csproj diff --git a/PettingZoo.Benchmark/PettingZoo.Benchmark.csproj b/PettingZoo.Benchmark/PettingZoo.Benchmark.csproj new file mode 100644 index 0000000..8297edf --- /dev/null +++ b/PettingZoo.Benchmark/PettingZoo.Benchmark.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + diff --git a/PettingZoo.Benchmark/Program.cs b/PettingZoo.Benchmark/Program.cs new file mode 100644 index 0000000..9579ed5 --- /dev/null +++ b/PettingZoo.Benchmark/Program.cs @@ -0,0 +1,138 @@ +using System.Text; +using System.Text.Json; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PettingZoo.Benchmark +{ + /* + + + Small JSON + + + | Method | Mean | Error | StdDev | Gen 0 | Allocated | + |----------------- |----------:|----------:|----------:|-------:|----------:| + | TestJsonConvert | 13.226 us | 0.1808 us | 0.1603 us | 3.6316 | 15 KB | + | TestJTokenParse | 12.360 us | 0.2453 us | 0.5010 us | 3.6011 | 15 KB | + | TestReaderWriter | 6.398 us | 0.1260 us | 0.1294 us | 2.0294 | 8 KB | + | TestJsonDocument | 4.400 us | 0.0758 us | 0.0902 us | 2.1019 | 9 KB | + + + + Large JSON (25 MB) + + | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | + |----------------- |-----------:|---------:|---------:|-----------:|-----------:|----------:|----------:| + | TestJsonConvert | 1,331.6 ms | 20.36 ms | 19.05 ms | 57000.0000 | 21000.0000 | 3000.0000 | 390 MB | + | TestJTokenParse | 1,411.0 ms | 27.28 ms | 24.18 ms | 62000.0000 | 23000.0000 | 4000.0000 | 415 MB | + | TestReaderWriter | 298.6 ms | 5.89 ms | 9.34 ms | 25000.0000 | 8000.0000 | 2000.0000 | 199 MB | + | TestJsonDocument | 278.5 ms | 5.29 ms | 6.30 ms | - | - | - | 246 MB | + + + */ + [MemoryDiagnoser] + public class JsonPrettyPrint + { + // Small Json file, which is likely to be typical for most RabbitMQ messages. + private const string Json = "{\"glossary\":{\"title\":\"example glossary\",\"GlossDiv\":{\"title\":\"S\",\"GlossList\":{\"GlossEntry\":{\"ID\":\"SGML\",\"SortAs\":\"SGML\",\"GlossTerm\":\"Standard Generalized Markup Language\",\"Acronym\":\"SGML\",\"Abbrev\":\"ISO 8879:1986\",\"GlossDef\":{\"para\":\"A meta-markup language, used to create markup languages such as DocBook.\",\"GlossSeeAlso\":[\"GML\",\"XML\"]},\"GlossSee\":\"markup\"}}}}}"; + + // To test with a large file instead, specify the file name here. + // For example, I've benchmarked it with this 25 MB JSON file: https://raw.githubusercontent.com/json-iterator/test-data/master/large-file.json + //private const string JsonFilename = ""; + private const string JsonFilename = "D:\\Temp\\large-file.json"; + + + private readonly string testJson; + + + public JsonPrettyPrint() + { + testJson = string.IsNullOrEmpty(JsonFilename) + ? Json + : File.ReadAllText(JsonFilename); + } + + + [Benchmark] + public string TestJsonConvert() + { + var obj = JsonConvert.DeserializeObject(testJson); + return JsonConvert.SerializeObject(obj, Formatting.Indented); + } + + + [Benchmark] + public string TestJTokenParse() + { + var obj = JToken.Parse(testJson); + return obj.ToString(Formatting.Indented); + } + + + [Benchmark] + public string? TestReaderWriter() + { + using var stringReader = new StringReader(testJson); + using var jsonTextReader = new JsonTextReader(stringReader); + using var stringWriter = new StringWriter(); + using var jsonWriter = new JsonTextWriter(stringWriter); + + jsonWriter.Formatting = Formatting.Indented; + + while (jsonTextReader.Read()) + jsonWriter.WriteToken(jsonTextReader); + + jsonWriter.Flush(); + return stringWriter.ToString(); + } + + + [Benchmark] + public string TestJsonDocument() + { + var doc = JsonDocument.Parse(testJson); + + using var stream = new MemoryStream(); + var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }); + doc.WriteTo(writer); + writer.Flush(); + + return Encoding.UTF8.GetString(stream.ToArray()); + } + + + + } + + + public class Program + { + public static void Main() + { + BenchmarkRunner.Run(); + + // To prove they all provide the correct output + /* + var prettyPrint = new JsonPrettyPrint(); + Console.WriteLine("JsonConvert"); + Console.WriteLine("-----------"); + Console.WriteLine(prettyPrint.TestJsonConvert()); + + Console.WriteLine("JToken"); + Console.WriteLine("------"); + Console.WriteLine(prettyPrint.TestJTokenParse()); + + Console.WriteLine("ReaderWriter"); + Console.WriteLine("------------"); + Console.WriteLine(prettyPrint.TestReaderWriter()); + + Console.WriteLine("JsonDocument"); + Console.WriteLine("------------"); + Console.WriteLine(prettyPrint.TestJsonDocument()); + */ + } + } +} \ No newline at end of file diff --git a/PettingZoo.Core/Rendering/MessageBodyRenderer.cs b/PettingZoo.Core/Rendering/MessageBodyRenderer.cs index 4482da1..b126f00 100644 --- a/PettingZoo.Core/Rendering/MessageBodyRenderer.cs +++ b/PettingZoo.Core/Rendering/MessageBodyRenderer.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace PettingZoo.Core.Rendering { @@ -26,8 +28,18 @@ namespace PettingZoo.Core.Rendering var bodyText = Encoding.UTF8.GetString(body); try { - var obj = JsonConvert.DeserializeObject(bodyText); - return JsonConvert.SerializeObject(obj, Formatting.Indented); + using var stringReader = new StringReader(bodyText); + using var jsonTextReader = new JsonTextReader(stringReader); + using var stringWriter = new StringWriter(); + using var jsonWriter = new JsonTextWriter(stringWriter); + + jsonWriter.Formatting = Formatting.Indented; + + while (jsonTextReader.Read()) + jsonWriter.WriteToken(jsonTextReader); + + jsonWriter.Flush(); + return stringWriter.ToString(); } catch { diff --git a/PettingZoo.Tapeti/PettingZoo.Tapeti_ooj5vuwa_wpftmp.csproj b/PettingZoo.Tapeti/PettingZoo.Tapeti_ooj5vuwa_wpftmp.csproj deleted file mode 100644 index 9e25216..0000000 --- a/PettingZoo.Tapeti/PettingZoo.Tapeti_ooj5vuwa_wpftmp.csproj +++ /dev/null @@ -1,325 +0,0 @@ - - - PettingZoo.Tapeti - obj\Debug\ - obj\ - P:\Development\PettingZoo\PettingZoo.Tapeti\obj\ - <_TargetAssemblyProjectName>PettingZoo.Tapeti - - - - net6.0-windows - 0.1 - true - enable - - - - - - - - - - - - - - - - - - - - - True - True - AssemblyParserStrings.resx - - - True - True - TapetiCmdImportExportStrings.resx - - - True - True - TapetiClassLibraryExampleGeneratorStrings.resx - - - True - True - ClassSelectionStrings.resx - - - True - True - PackageSelectionStrings.resx - - - - - ResXFileCodeGenerator - AssemblyParserStrings.Designer.cs - - - ResXFileCodeGenerator - TapetiCmdImportExportStrings.Designer.cs - - - ResXFileCodeGenerator - TapetiClassLibraryExampleGeneratorStrings.Designer.cs - - - PublicResXFileCodeGenerator - ClassSelectionStrings.Designer.cs - - - PublicResXFileCodeGenerator - PackageSelectionStrings.Designer.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/PettingZoo.sln b/PettingZoo.sln index e840afc..e606ce1 100644 --- a/PettingZoo.sln +++ b/PettingZoo.sln @@ -20,7 +20,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Settings.LiteDB" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.WPF", "PettingZoo.WPF\PettingZoo.WPF.csproj", "{E6617B69-2AC4-4056-B801-DD32E2374B71}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.Test", "PettingZoo.Test\PettingZoo.Test.csproj", "{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Test", "PettingZoo.Test\PettingZoo.Test.csproj", "{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.Benchmark", "PettingZoo.Benchmark\PettingZoo.Benchmark.csproj", "{C25BC83A-D302-46D2-97F6-5F888B824E2D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -56,6 +58,10 @@ Global {3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Debug|Any CPU.Build.0 = Debug|Any CPU {3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.Build.0 = Release|Any CPU + {C25BC83A-D302-46D2-97F6-5F888B824E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C25BC83A-D302-46D2-97F6-5F888B824E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C25BC83A-D302-46D2-97F6-5F888B824E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C25BC83A-D302-46D2-97F6-5F888B824E2D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PettingZoo/UI/Tab/Publisher/PayloadEditorControl.xaml.cs b/PettingZoo/UI/Tab/Publisher/PayloadEditorControl.xaml.cs index 45925fc..64ded54 100644 --- a/PettingZoo/UI/Tab/Publisher/PayloadEditorControl.xaml.cs +++ b/PettingZoo/UI/Tab/Publisher/PayloadEditorControl.xaml.cs @@ -123,16 +123,15 @@ namespace PettingZoo.UI.Tab.Publisher } - private IPayloadMacroProcessor? macroProcessor; public IPayloadMacroProcessor? MacroProcessor { - get => macroProcessor; + get => viewModel.MacroProcessor; set { - if (value == macroProcessor) + if (value == viewModel.MacroProcessor) return; - macroProcessor = value; + viewModel.MacroProcessor = value; UpdateMacroContextMenu(); } } @@ -266,10 +265,10 @@ namespace PettingZoo.UI.Tab.Publisher { ContextMenuInsertMacro.Items.Clear(); - if (macroProcessor == null) + if (MacroProcessor == null) return; - foreach (var macro in macroProcessor.Macros) + foreach (var macro in MacroProcessor.Macros) { var macroMenuItem = new MenuItem { diff --git a/PettingZoo/UI/Tab/Publisher/PayloadEditorViewModel.cs b/PettingZoo/UI/Tab/Publisher/PayloadEditorViewModel.cs index d531539..8d9dbff 100644 --- a/PettingZoo/UI/Tab/Publisher/PayloadEditorViewModel.cs +++ b/PettingZoo/UI/Tab/Publisher/PayloadEditorViewModel.cs @@ -2,10 +2,10 @@ using System.ComponentModel; using System.Reactive.Linq; using System.Windows; -using System.Windows.Input; using ICSharpCode.AvalonEdit.Highlighting; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using PettingZoo.Core.Macros; using PettingZoo.Core.Validation; using PettingZoo.WPF.ViewModel; @@ -159,6 +159,7 @@ namespace PettingZoo.UI.Tab.Publisher public IPayloadValidator? Validator { get; set; } + public IPayloadMacroProcessor? MacroProcessor { get; set; } public PayloadEditorViewModel() @@ -166,7 +167,7 @@ namespace PettingZoo.UI.Tab.Publisher var observable = Observable.FromEventPattern( h => PropertyChanged += h, h => PropertyChanged -= h) - .Where(e => e.EventArgs.PropertyName == nameof(Payload)); + .Where(e => e.EventArgs.PropertyName is nameof(Payload) or nameof(EnableMacros)); observable .Subscribe(_ => ValidatingPayload()); @@ -204,14 +205,18 @@ namespace PettingZoo.UI.Tab.Publisher { if (!string.IsNullOrEmpty(Payload)) { + var validatePayload = EnableMacros && MacroProcessor != null + ? MacroProcessor.Apply(Payload) + : Payload; + if (Validator != null && Validator.CanValidate()) { - Validator.Validate(payload); + Validator.Validate(validatePayload); ValidationInfo = new ValidationInfo(ValidationStatus.Ok); } else { - JToken.Parse(Payload); + JToken.Parse(validatePayload); ValidationInfo = new ValidationInfo(ValidationStatus.OkSyntax); } }