diff --git a/Benchmarks/AnalyzeBenchmark.cs b/AppInspector.Benchmarks/AnalyzeBenchmark.cs
similarity index 100%
rename from Benchmarks/AnalyzeBenchmark.cs
rename to AppInspector.Benchmarks/AnalyzeBenchmark.cs
diff --git a/Benchmarks/Benchmarks.csproj b/AppInspector.Benchmarks/AppInspector.Benchmarks.csproj
similarity index 82%
rename from Benchmarks/Benchmarks.csproj
rename to AppInspector.Benchmarks/AppInspector.Benchmarks.csproj
index 175b705a..3beb2e71 100644
--- a/Benchmarks/Benchmarks.csproj
+++ b/AppInspector.Benchmarks/AppInspector.Benchmarks.csproj
@@ -5,6 +5,8 @@
net6.0;net7.0
enable
10.0
+ ApplicationInspector.Benchmarks
+ Microsoft Corporation
@@ -13,6 +15,7 @@
+
diff --git a/Benchmarks/DistinctBenchmarks.cs b/AppInspector.Benchmarks/DistinctBenchmarks.cs
similarity index 100%
rename from Benchmarks/DistinctBenchmarks.cs
rename to AppInspector.Benchmarks/DistinctBenchmarks.cs
diff --git a/Benchmarks/Program.cs b/AppInspector.Benchmarks/Program.cs
similarity index 84%
rename from Benchmarks/Program.cs
rename to AppInspector.Benchmarks/Program.cs
index 088cce85..bb77cb6b 100644
--- a/Benchmarks/Program.cs
+++ b/AppInspector.Benchmarks/Program.cs
@@ -9,6 +9,6 @@ public static void Main(string[] args)
{
// new DebugInProcessConfig()
//BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig());
- var summary = BenchmarkRunner.Run();
+ var summary = BenchmarkRunner.Run();
}
}
\ No newline at end of file
diff --git a/AppInspector.Benchmarks/WriterBench.cs b/AppInspector.Benchmarks/WriterBench.cs
new file mode 100644
index 00000000..ac3d8605
--- /dev/null
+++ b/AppInspector.Benchmarks/WriterBench.cs
@@ -0,0 +1,71 @@
+using System;
+using System.IO;
+using System.Reflection;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using DotLiquid;
+using Microsoft.ApplicationInspector.CLI;
+using Microsoft.ApplicationInspector.Commands;
+using Microsoft.ApplicationInspector.RulesEngine;
+
+namespace ApplicationInspector.Benchmarks;
+[MemoryDiagnoser]
+[SimpleJob(RuntimeMoniker.Net70)]
+public class WriterBench
+{
+ [Params(1000, 10000)]
+ public int N;
+
+ // Holds the result object which will be serialized
+ private AnalyzeResult _result;
+
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ var _exerpt = "Hello World";
+ var helper = new MetaDataHelper("..");
+ var matchRecord = new MatchRecord("rule-id", "rule-name")
+ {
+ Boundary = new Boundary() { Index = 0, Length = 1 },
+ EndLocationColumn = 0,
+ EndLocationLine = 1,
+ Excerpt = _exerpt,
+ FileName = "TestFile",
+ LanguageInfo = new LanguageInfo(),
+ Tags = new []{"TestTag"}
+ };
+ for (int i = 0; i < N; i++)
+ {
+ helper.AddMatchRecord(matchRecord);
+ }
+ helper.PrepareReport();
+
+ _result = new AnalyzeResult() { Metadata = helper.Metadata, ResultCode = 0 };
+ }
+
+ [Benchmark(Baseline = true)]
+ public void ExportRecordsToJson()
+ {
+ var tmpPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ CLIAnalyzeCmdOptions analyzeOpts = new CLIAnalyzeCmdOptions()
+ {
+ OutputFileFormat = "json",
+ OutputFilePath = tmpPath
+ };
+ var writerFactory = new WriterFactory();
+ var writer = writerFactory.GetWriter(analyzeOpts);
+ writer.WriteResults(_result,analyzeOpts);
+ File.Delete(tmpPath);
+ }
+
+ public static string GetExecutingDirectoryName()
+ {
+ if (Assembly.GetEntryAssembly()?.GetName().CodeBase is string codeBaseLoc)
+ {
+ var location = new Uri(codeBaseLoc);
+ return new FileInfo(location.AbsolutePath).Directory?.FullName ?? string.Empty;
+ }
+
+ return string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/AppInspector.CLI/TagInfo.cs b/AppInspector.CLI/TagInfo.cs
index bb47bd64..3a94d546 100644
--- a/AppInspector.CLI/TagInfo.cs
+++ b/AppInspector.CLI/TagInfo.cs
@@ -3,10 +3,10 @@
using System;
using System.Collections.Generic;
+using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using DotLiquid;
using Microsoft.ApplicationInspector.RulesEngine;
-using Newtonsoft.Json;
namespace Microsoft.ApplicationInspector.Commands;
@@ -21,17 +21,18 @@ public enum tagInfoType
allTags
}
- [JsonProperty(PropertyName = "type")] public tagInfoType Type;
+ [JsonPropertyName("type")]
+ public tagInfoType Type;
public TagCategory()
{
Groups = new List();
}
- [JsonProperty(PropertyName = "categoryName")]
+ [JsonPropertyName("categoryName")]
public string? Name { get; set; }
- [JsonProperty(PropertyName = "groups")]
+ [JsonPropertyName("groups")]
public List? Groups { get; set; }
}
@@ -45,14 +46,14 @@ public TagGroup()
Patterns = new List();
}
- [JsonProperty(PropertyName = "title")] public string? Title { get; set; }
+ [JsonPropertyName("title")] public string? Title { get; set; }
[JsonIgnore] public string? IconURL { get; set; }
- [JsonProperty(PropertyName = "dataRef")]
+ [JsonPropertyName("dataRef")]
public string? DataRef { get; set; }
- [JsonProperty(PropertyName = "patterns")]
+ [JsonPropertyName("patterns")]
public List? Patterns { get; set; }
}
@@ -61,7 +62,7 @@ public class TagSearchPattern : Drop
private Regex? _expression;
private string _searchPattern = "";
- [JsonProperty(PropertyName = "searchPattern")]
+ [JsonPropertyName("searchPattern")]
public string SearchPattern
{
get => _searchPattern;
@@ -85,19 +86,19 @@ public Regex Expression
}
}
- [JsonProperty(PropertyName = "displayName")]
+ [JsonPropertyName("displayName")]
public string? DisplayName { get; set; }
- [JsonProperty(PropertyName = "detectedIcon")]
+ [JsonPropertyName("detectedIcon")]
public string? DetectedIcon { get; set; } = "fas fa-cat"; //default
- [JsonProperty(PropertyName = "notDetectedIcon")]
+ [JsonPropertyName("notDetectedIcon")]
public string? NotDetectedIcon { get; set; }
- [JsonProperty(PropertyName = "detected")]
+ [JsonPropertyName("detected")]
public bool Detected { get; set; }
- [JsonProperty(PropertyName = "details")]
+ [JsonPropertyName("details")]
public string Details
{
get
@@ -107,7 +108,7 @@ public string Details
}
}
- [JsonProperty(PropertyName = "confidence")]
+ [JsonPropertyName("confidence")]
public string Confidence { get; set; } = "Medium";
public static bool ShouldSerializeExpression()
@@ -123,14 +124,14 @@ public class TagInfo : Drop
{
private string _confidence = "Medium";
- [JsonProperty(PropertyName = "tag")] public string? Tag { get; set; }
+ [JsonPropertyName("tag")] public string? Tag { get; set; }
- [JsonProperty(PropertyName = "displayName")]
+ [JsonPropertyName("displayName")]
public string? ShortTag { get; set; }
[JsonIgnore] public string? StatusIcon { get; set; }
- [JsonProperty(PropertyName = "confidence")]
+ [JsonPropertyName("confidence")]
public string Confidence
{
get => _confidence;
@@ -143,14 +144,14 @@ public string Confidence
}
}
- [JsonProperty(PropertyName = "severity")]
+ [JsonPropertyName("severity")]
public string Severity { get; set; } = "Moderate";
- [JsonProperty(PropertyName = "detected")]
+ [JsonPropertyName("detected")]
public bool Detected { get; set; }
}
public class TagException
{
- [JsonProperty(PropertyName = "tag")] public string? Tag { get; set; }
+ [JsonPropertyName("tag")] public string? Tag { get; set; }
}
\ No newline at end of file
diff --git a/AppInspector.CLI/Writers/AnalyzeHtmlWriter.cs b/AppInspector.CLI/Writers/AnalyzeHtmlWriter.cs
index 835e46c2..6944fa0e 100644
--- a/AppInspector.CLI/Writers/AnalyzeHtmlWriter.cs
+++ b/AppInspector.CLI/Writers/AnalyzeHtmlWriter.cs
@@ -16,7 +16,8 @@
using Microsoft.ApplicationInspector.RulesEngine;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
+using System.Text.Json;
namespace Microsoft.ApplicationInspector.CLI;
@@ -27,7 +28,7 @@ public class AnalyzeHtmlWriter : CommandResultsWriter
private MetaData? _appMetaData;
- public AnalyzeHtmlWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter)
+ public AnalyzeHtmlWriter(StreamWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter)
{
_logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
KeyedTagInfoLists = new Dictionary>();
@@ -91,7 +92,7 @@ private void WriteHtmlResult()
string? jsonData;
try
{
- jsonData = JsonConvert.SerializeObject(data);
+ jsonData = JsonSerializer.Serialize(data);
}
catch (Exception e)
{
@@ -232,9 +233,23 @@ public void PopulateTagGroups()
//read default/user preferences on what tags to report presence on and groupings
if (File.Exists(Utils.GetPath(Utils.AppPath.tagGroupPref)))
{
- TagGroupPreferences =
- JsonConvert.DeserializeObject>(
- File.ReadAllText(Utils.GetPath(Utils.AppPath.tagGroupPref)));
+ try
+ {
+ var options = new JsonSerializerOptions
+ {
+ ReadCommentHandling = JsonCommentHandling.Skip, // Allow strings to start with '/', e.g., "// Copyright (C) Microsoft. All rights reserved"
+ };
+ TagGroupPreferences =
+ JsonSerializer.Deserialize>(
+ File.ReadAllText(Utils.GetPath(Utils.AppPath.tagGroupPref)), options);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(
+ "Failed to populate tag groups. Failed to serialize JSON representation of results in memory. {Type} : {Message}",
+ e.GetType().Name, e.Message);
+ throw;
+ }
}
else
{
@@ -627,13 +642,15 @@ public List ConvertTagCounters(IEnumerable metri
///
public class TagCounterUI : Drop
{
- [JsonProperty(PropertyName = "tag")] public string? Tag { get; set; }
+ [JsonPropertyName("tag")]
+ public string? Tag { get; set; }
- [JsonProperty(PropertyName = "displayName")]
+ [JsonPropertyName("displayName")]
public string? ShortTag { get; set; }
- [JsonProperty(PropertyName = "count")] public int Count { get; set; }
+ [JsonPropertyName("count")]
+ public int Count { get; set; }
- [JsonProperty(PropertyName = "includeAsMatch")]
+ [JsonPropertyName("includeAsMatch")]
public bool IncludeAsMatch => false;
}
\ No newline at end of file
diff --git a/AppInspector.CLI/Writers/AnalyzeJsonWriter.cs b/AppInspector.CLI/Writers/AnalyzeJsonWriter.cs
index eb317ef0..39621e41 100644
--- a/AppInspector.CLI/Writers/AnalyzeJsonWriter.cs
+++ b/AppInspector.CLI/Writers/AnalyzeJsonWriter.cs
@@ -5,7 +5,9 @@
using Microsoft.ApplicationInspector.Commands;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
+using System.Text.Json;
+using System;
namespace Microsoft.ApplicationInspector.CLI;
@@ -20,7 +22,7 @@ public class AnalyzeJsonWriter : CommandResultsWriter
{
private readonly ILogger _logger;
- public AnalyzeJsonWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter)
+ public AnalyzeJsonWriter(StreamWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter)
{
_logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
@@ -28,12 +30,25 @@ public AnalyzeJsonWriter(TextWriter textWriter, ILoggerFactory? loggerFactory =
public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true)
{
var analyzeResult = (AnalyzeResult)result;
+ if (StreamWriter == null)
+ {
+ throw new ArgumentNullException(nameof(StreamWriter));
+ }
- JsonSerializer jsonSerializer = new();
- jsonSerializer.Formatting = Formatting.Indented;
- if (TextWriter != null)
+ try
+ {
+ var options = new JsonSerializerOptions
+ {
+ WriteIndented = true,
+ };
+ JsonSerializer.Serialize(StreamWriter.BaseStream, analyzeResult, options);
+ }
+ catch (Exception e)
{
- jsonSerializer.Serialize(TextWriter, analyzeResult);
+ _logger.LogError(
+ "Failed to serialize JSON representation of results in memory. {Type} : {Message}",
+ e.GetType().Name, e.Message);
+ throw;
}
if (autoClose)
@@ -47,6 +62,7 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
///
private class TagsFile
{
- [JsonProperty(PropertyName = "tags")] public string[]? Tags { get; set; }
+ [JsonPropertyName("tags")]
+ public string[]? Tags { get; set; }
}
}
\ No newline at end of file
diff --git a/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs b/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs
index 2c2a019b..37adfca2 100644
--- a/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs
+++ b/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs
@@ -5,13 +5,13 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text.Json;
using Microsoft.ApplicationInspector.Commands;
using Microsoft.ApplicationInspector.RulesEngine;
using Microsoft.CodeAnalysis.Sarif;
using Microsoft.CST.OAT.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
using Location = Microsoft.CodeAnalysis.Sarif.Location;
using Result = Microsoft.ApplicationInspector.Commands.Result;
@@ -37,16 +37,16 @@ public class AnalyzeSarifWriter : CommandResultsWriter
{
private readonly ILogger _logger;
- public AnalyzeSarifWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter)
+ public AnalyzeSarifWriter(StreamWriter streamWriter, ILoggerFactory? loggerFactory = null) : base(streamWriter)
{
_logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true)
{
- if (TextWriter is null)
+ if (StreamWriter is null)
{
- throw new NullReferenceException(nameof(TextWriter));
+ throw new ArgumentNullException(nameof(StreamWriter));
}
string? basePath = null;
@@ -186,11 +186,22 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
run.Results.Add(sarifResult);
}
- log.Runs.Add(run);
- JsonSerializerSettings serializerSettings = new();
- var serializer = new JsonSerializer();
- serializer.Serialize(TextWriter, log);
- FlushAndClose();
+ log.Runs.Add(run);
+ try
+ {
+ JsonSerializer.Serialize(StreamWriter.BaseStream, log);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(
+ "Failed to serialize JSON representation of results in memory. {Type} : {Message}",
+ e.GetType().Name, e.Message);
+ throw;
+ }
+ if (autoClose)
+ {
+ FlushAndClose();
+ }
}
else
{
diff --git a/AppInspector.CLI/Writers/CmdResultsWriter.cs b/AppInspector.CLI/Writers/CmdResultsWriter.cs
index 96889927..13200b13 100644
--- a/AppInspector.CLI/Writers/CmdResultsWriter.cs
+++ b/AppInspector.CLI/Writers/CmdResultsWriter.cs
@@ -17,6 +17,12 @@ protected CommandResultsWriter(TextWriter writer)
}
public TextWriter TextWriter { get; }
+
+ public StreamWriter? StreamWriter
+ {
+ get { return (StreamWriter)TextWriter; }
+ }
+
public abstract void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true);
public void FlushAndClose()
diff --git a/AppInspector.CLI/Writers/JsonWriter.cs b/AppInspector.CLI/Writers/JsonWriter.cs
index 9b310fab..7cb8f194 100644
--- a/AppInspector.CLI/Writers/JsonWriter.cs
+++ b/AppInspector.CLI/Writers/JsonWriter.cs
@@ -1,9 +1,11 @@
using System;
using System.IO;
+using System.Net.Sockets;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using Microsoft.ApplicationInspector.Commands;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
namespace Microsoft.ApplicationInspector.CLI.Writers;
@@ -11,35 +13,48 @@ internal class JsonWriter : CommandResultsWriter
{
private readonly ILogger _logger;
- internal JsonWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter)
+ internal JsonWriter(StreamWriter streamWriter, ILoggerFactory? loggerFactory = null) : base(streamWriter)
{
_logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true)
{
- JsonSerializer jsonSerializer = new();
- jsonSerializer.Formatting = Formatting.Indented;
- jsonSerializer.NullValueHandling = NullValueHandling.Ignore;
- jsonSerializer.DefaultValueHandling = DefaultValueHandling.Ignore;
+ var options = new JsonSerializerOptions
+ {
+ WriteIndented = true,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, // The WhenWritingDefault setting also prevents serialization of null-value reference type and nullable value type properties.
+ // jsonSerializer.NullValueHandling = NullValueHandling.Ignore;
+ // jsonSerializer.DefaultValueHandling = DefaultValueHandling.Ignore;
+ };
- if (TextWriter is null)
+ if (StreamWriter is null)
{
throw new ArgumentNullException(nameof(TextWriter));
}
- switch (result)
+ try
+ {
+ switch (result)
+ {
+ case TagDiffResult:
+ case ExportTagsResult:
+ case VerifyRulesResult:
+ JsonSerializer.Serialize(StreamWriter.BaseStream, result, options);
+ break;
+ case PackRulesResult prr:
+ JsonSerializer.Serialize(StreamWriter.BaseStream, prr.Rules, options);
+ break;
+ default:
+ throw new Exception("Unexpected object type for json writer");
+ }
+ }
+ catch (Exception e)
{
- case TagDiffResult:
- case ExportTagsResult:
- case VerifyRulesResult:
- jsonSerializer.Serialize(TextWriter, result);
- break;
- case PackRulesResult prr:
- jsonSerializer.Serialize(TextWriter, prr.Rules);
- break;
- default:
- throw new Exception("Unexpected object type for json writer");
+ _logger.LogError(
+ "Failed to serialize JSON representation of results in memory. {Type} : {Message}",
+ e.GetType().Name, e.Message);
+ throw;
}
if (autoClose)
diff --git a/AppInspector.CLI/Writers/WriterFactory.cs b/AppInspector.CLI/Writers/WriterFactory.cs
index 2c094ce2..5c6a1241 100644
--- a/AppInspector.CLI/Writers/WriterFactory.cs
+++ b/AppInspector.CLI/Writers/WriterFactory.cs
@@ -49,21 +49,21 @@ public CommandResultsWriter GetWriter(CLICommandOptions options)
///
///
private CommandResultsWriter GetAnalyzeWriter(CLIAnalyzeCmdOptions options)
- {
- var textWriter = GetTextWriter(options.OutputFilePath);
+ {
+ var streamWriter = GetStreamWriter(options.OutputFilePath);
return options.OutputFileFormat.ToLower() switch
{
- "json" => new AnalyzeJsonWriter(textWriter, _loggerFactory),
- "text" => new AnalyzeTextWriter(textWriter, options.TextOutputFormat, _loggerFactory),
- "html" => new AnalyzeHtmlWriter(textWriter, _loggerFactory),
- "sarif" => new AnalyzeSarifWriter(textWriter, _loggerFactory),
+ "json" => new AnalyzeJsonWriter(streamWriter, _loggerFactory),
+ "text" => new AnalyzeTextWriter(streamWriter, options.TextOutputFormat, _loggerFactory),
+ "html" => new AnalyzeHtmlWriter(streamWriter, _loggerFactory),
+ "sarif" => new AnalyzeSarifWriter(streamWriter, _loggerFactory),
_ => throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f"))
};
}
public CommandResultsWriter GetExportTagsWriter(CLIExportTagsCmdOptions options)
{
- var writer = GetTextWriter(options.OutputFilePath);
+ var writer = GetStreamWriter(options.OutputFilePath);
return options.OutputFileFormat.ToLower() switch
{
"json" => new JsonWriter(writer, _loggerFactory),
@@ -74,7 +74,7 @@ public CommandResultsWriter GetExportTagsWriter(CLIExportTagsCmdOptions options)
private CommandResultsWriter GetTagDiffWriter(CLITagDiffCmdOptions options)
{
- var writer = GetTextWriter(options.OutputFilePath);
+ var writer = GetStreamWriter(options.OutputFilePath);
return options.OutputFileFormat.ToLower() switch
{
"json" => new JsonWriter(writer, _loggerFactory),
@@ -85,7 +85,7 @@ private CommandResultsWriter GetTagDiffWriter(CLITagDiffCmdOptions options)
private CommandResultsWriter GetVerifyRulesWriter(CLIVerifyRulesCmdOptions options)
{
- var writer = GetTextWriter(options.OutputFilePath);
+ var writer = GetStreamWriter(options.OutputFilePath);
return options.OutputFileFormat.ToLower() switch
{
"json" => new JsonWriter(writer, _loggerFactory),
@@ -96,7 +96,7 @@ private CommandResultsWriter GetVerifyRulesWriter(CLIVerifyRulesCmdOptions optio
private CommandResultsWriter GetPackRulesWriter(CLIPackRulesCmdOptions options)
{
- var writer = GetTextWriter(options.OutputFilePath);
+ var writer = GetStreamWriter(options.OutputFilePath);
return options.OutputFileFormat.ToLower() switch
{
"json" => new JsonWriter(writer, _loggerFactory),
@@ -109,6 +109,7 @@ private CommandResultsWriter GetPackRulesWriter(CLIPackRulesCmdOptions options)
///
/// The path to create, if null or empty will use Console.Out.
///
+ [Obsolete("Use GetStreamWriter instead.")]
private TextWriter GetTextWriter(string? outputFileName)
{
TextWriter textWriter;
@@ -131,4 +132,34 @@ private TextWriter GetTextWriter(string? outputFileName)
return textWriter;
}
+
+ ///
+ /// Create a StreamWriter for the given path or console.
+ ///
+ /// The path to create, if null or empty will use console output.
+ ///
+ private StreamWriter GetStreamWriter(string? outputFileName)
+ {
+ StreamWriter streamWriter;
+ if (string.IsNullOrEmpty(outputFileName))
+ {
+ streamWriter = new StreamWriter(Console.OpenStandardOutput());
+ streamWriter.AutoFlush = true;
+ Console.SetOut(streamWriter);
+ }
+ else
+ {
+ try
+ {
+ streamWriter = File.CreateText(outputFileName);
+ }
+ catch (Exception)
+ {
+ _logger.LogError(MsgHelp.GetString(MsgHelp.ID.CMD_INVALID_FILE_OR_DIR), outputFileName);
+ throw;
+ }
+ }
+
+ return streamWriter;
+ }
}
\ No newline at end of file
diff --git a/AppInspector.RulesEngine/AppInspector.RulesEngine.csproj b/AppInspector.RulesEngine/AppInspector.RulesEngine.csproj
index 3762de1b..0da94793 100644
--- a/AppInspector.RulesEngine/AppInspector.RulesEngine.csproj
+++ b/AppInspector.RulesEngine/AppInspector.RulesEngine.csproj
@@ -35,7 +35,6 @@
-
diff --git a/AppInspector.RulesEngine/Comment.cs b/AppInspector.RulesEngine/Comment.cs
index 2e08a05f..fe795960 100644
--- a/AppInspector.RulesEngine/Comment.cs
+++ b/AppInspector.RulesEngine/Comment.cs
@@ -1,7 +1,7 @@
// Copyright(C) Microsoft.All rights reserved.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.RulesEngine;
@@ -10,15 +10,15 @@ namespace Microsoft.ApplicationInspector.RulesEngine;
///
internal class Comment
{
- [JsonProperty(PropertyName = "language")]
+ [JsonPropertyName("language")]
public string[]? Languages { get; set; }
- [JsonProperty(PropertyName = "inline")]
+ [JsonPropertyName("inline")]
public string? Inline { get; set; }
- [JsonProperty(PropertyName = "prefix")]
+ [JsonPropertyName("prefix")]
public string? Prefix { get; set; }
- [JsonProperty(PropertyName = "suffix")]
+ [JsonPropertyName("suffix")]
public string? Suffix { get; set; }
}
\ No newline at end of file
diff --git a/AppInspector.RulesEngine/Confidence.cs b/AppInspector.RulesEngine/Confidence.cs
index 654aeabe..960774f4 100644
--- a/AppInspector.RulesEngine/Confidence.cs
+++ b/AppInspector.RulesEngine/Confidence.cs
@@ -1,11 +1,10 @@
using System;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.RulesEngine;
[Flags]
-[JsonConverter(typeof(StringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Confidence
{
Unspecified = 0,
diff --git a/AppInspector.RulesEngine/LanguageInfo.cs b/AppInspector.RulesEngine/LanguageInfo.cs
index 82b449f6..13f6ccfe 100644
--- a/AppInspector.RulesEngine/LanguageInfo.cs
+++ b/AppInspector.RulesEngine/LanguageInfo.cs
@@ -1,8 +1,7 @@
// Copyright(C) Microsoft.All rights reserved.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.RulesEngine;
@@ -17,15 +16,16 @@ public enum LangFileType
Build
}
- [JsonProperty(PropertyName = "name")] public string Name { get; set; } = "";
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = "";
- [JsonProperty(PropertyName = "extensions")]
+ [JsonPropertyName("extensions")]
public string[]? Extensions { get; set; }
- [JsonProperty(PropertyName = "file-names")]
+ [JsonPropertyName("file-names")]
public string[]? FileNames { get; set; }
- [JsonProperty(PropertyName = "type")]
- [JsonConverter(typeof(StringEnumConverter))]
+ [JsonPropertyName("type")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
public LangFileType Type { get; set; } = LangFileType.Code;
}
\ No newline at end of file
diff --git a/AppInspector.RulesEngine/Languages.cs b/AppInspector.RulesEngine/Languages.cs
index 04b2da6d..51f8d9da 100644
--- a/AppInspector.RulesEngine/Languages.cs
+++ b/AppInspector.RulesEngine/Languages.cs
@@ -5,9 +5,9 @@
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
namespace Microsoft.ApplicationInspector.RulesEngine;
@@ -18,8 +18,8 @@ public sealed class Languages
{
private const string CommentResourcePath = "Microsoft.ApplicationInspector.RulesEngine.Resources.comments.json";
private const string LanguagesResourcePath = "Microsoft.ApplicationInspector.RulesEngine.Resources.languages.json";
- private readonly List _comments;
- private readonly List _languageInfos;
+ private readonly List _comments = new();
+ private readonly List _languageInfos = new();
private readonly ILogger _logger;
public Languages(ILoggerFactory? loggerFactory = null, Stream? commentsStream = null,
@@ -33,14 +33,14 @@ public Languages(ILoggerFactory? loggerFactory = null, Stream? commentsStream =
{
_logger.LogError("Failed to load embedded comments configuration from {CommentResourcePath}",
CommentResourcePath);
- _comments = new List();
}
else
{
- using StreamReader commentStreamReader = new(commentResource);
- using JsonReader commentJsonReader = new JsonTextReader(commentStreamReader);
- JsonSerializer jsonSerializer = new();
- _comments = jsonSerializer.Deserialize>(commentJsonReader) ?? new List();
+ var result = JsonSerializer.DeserializeAsync>(commentResource);
+ if (result.Result != null)
+ {
+ _comments = result.Result.ToList();
+ }
}
var languagesResource = languagesStream ?? assembly.GetManifestResourceStream(LanguagesResourcePath);
@@ -48,15 +48,14 @@ public Languages(ILoggerFactory? loggerFactory = null, Stream? commentsStream =
{
_logger.LogError("Failed to load embedded languages configuration from {LanguagesResourcePath}",
LanguagesResourcePath);
- _languageInfos = new List();
}
else
{
- using StreamReader languagesStreamReader = new(languagesResource);
- using JsonReader languagesJsonReader = new JsonTextReader(languagesStreamReader);
- JsonSerializer jsonSerializer = new();
- _languageInfos = jsonSerializer.Deserialize>(languagesJsonReader) ??
- new List();
+ var result = JsonSerializer.DeserializeAsync>(languagesResource);
+ if (result.Result != null)
+ {
+ _languageInfos = result.Result.ToList();
+ }
}
}
diff --git a/AppInspector.RulesEngine/MatchRecord.cs b/AppInspector.RulesEngine/MatchRecord.cs
index ad834c5f..fd69bc88 100644
--- a/AppInspector.RulesEngine/MatchRecord.cs
+++ b/AppInspector.RulesEngine/MatchRecord.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.RulesEngine;
@@ -37,28 +37,28 @@ public MatchRecord(string ruleId, string ruleName)
///
/// Rule Id found in matching rule
///
- [JsonProperty(PropertyName = "ruleId")]
+ [JsonPropertyName("ruleId")]
[ExcludeFromCodeCoverage]
public string RuleId { get; set; }
///
/// Rule name found in matching rule
///
- [JsonProperty(PropertyName = "ruleName")]
+ [JsonPropertyName("ruleName")]
[ExcludeFromCodeCoverage]
public string RuleName { get; set; }
///
/// Rule description found in matching rule
///
- [JsonProperty(PropertyName = "ruleDescription")]
+ [JsonPropertyName("ruleDescription")]
[ExcludeFromCodeCoverage]
public string? RuleDescription { get; set; }
///
/// Tags in matching rule
///
- [JsonProperty(PropertyName = "tags")]
+ [JsonPropertyName("tags")]
[ExcludeFromCodeCoverage]
public string[]? Tags { get; set; }
@@ -66,7 +66,7 @@ public MatchRecord(string ruleId, string ruleName)
/// Rule severity
///
/// _rule
- [JsonProperty(PropertyName = "severity")]
+ [JsonPropertyName("severity")]
[ExcludeFromCodeCoverage]
public Severity Severity { get; set; }
@@ -75,25 +75,26 @@ public MatchRecord(string ruleId, string ruleName)
///
/// Matching pattern found in matching rule
///
- [JsonProperty(PropertyName = "pattern")]
+ [JsonPropertyName("pattern")]
[ExcludeFromCodeCoverage]
public string? Pattern => MatchingPattern?.Pattern;
///
/// Pattern confidence in matching rule pattern
///
- [JsonProperty(PropertyName = "confidence")]
+ [JsonPropertyName("confidence")]
[ExcludeFromCodeCoverage]
public Confidence Confidence => MatchingPattern?.Confidence ?? Confidence.Unspecified;
///
/// Pattern type of matching pattern
///
- [JsonProperty(PropertyName = "type")]
+ [JsonPropertyName("type")]
[ExcludeFromCodeCoverage]
public string? PatternType => MatchingPattern?.PatternType.ToString();
- [JsonIgnore] [ExcludeFromCodeCoverage] public TextContainer? FullTextContainer { get; set; }
+ [JsonIgnore] [ExcludeFromCodeCoverage]
+ public TextContainer? FullTextContainer { get; set; }
///
/// Internal to namespace only
@@ -105,27 +106,27 @@ public MatchRecord(string ruleId, string ruleName)
///
/// Friendly source type
///
- [JsonProperty(PropertyName = "language")]
+ [JsonPropertyName("language")]
public string? Language => LanguageInfo?.Name;
///
/// Filename of this match
///
- [JsonProperty(PropertyName = "fileName")]
+ [JsonPropertyName("fileName")]
[ExcludeFromCodeCoverage]
public string? FileName { get; set; }
///
/// Matching text for this record
///
- [JsonProperty(PropertyName = "sample")]
+ [JsonPropertyName("sample")]
[ExcludeFromCodeCoverage]
public string Sample { get; set; } = "";
///
/// Matching surrounding context text for sample in this record
///
- [JsonProperty(PropertyName = "excerpt")]
+ [JsonPropertyName("excerpt")]
[ExcludeFromCodeCoverage]
public string Excerpt { get; set; } = "";
@@ -134,28 +135,28 @@ public MatchRecord(string ruleId, string ruleName)
///
/// Starting line location of the matching text
///
- [JsonProperty(PropertyName = "startLocationLine")]
+ [JsonPropertyName("startLocationLine")]
[ExcludeFromCodeCoverage]
public int StartLocationLine { get; set; }
///
/// Starting column location of the matching text
///
- [JsonProperty(PropertyName = "startLocationColumn")]
+ [JsonPropertyName("startLocationColumn")]
[ExcludeFromCodeCoverage]
public int StartLocationColumn { get; set; }
///
/// Ending line location of the matching text
///
- [JsonProperty(PropertyName = "endLocationLine")]
+ [JsonPropertyName("endLocationLine")]
[ExcludeFromCodeCoverage]
public int EndLocationLine { get; set; }
///
/// Ending column of the matching text
///
- [JsonProperty(PropertyName = "endLocationColumn")]
+ [JsonPropertyName("endLocationColumn")]
[ExcludeFromCodeCoverage]
public int EndLocationColumn { get; set; }
}
\ No newline at end of file
diff --git a/AppInspector.RulesEngine/PatternScope.cs b/AppInspector.RulesEngine/PatternScope.cs
index b79479ca..d39dded1 100644
--- a/AppInspector.RulesEngine/PatternScope.cs
+++ b/AppInspector.RulesEngine/PatternScope.cs
@@ -1,11 +1,10 @@
// Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License.
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.RulesEngine;
-[JsonConverter(typeof(StringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum PatternScope
{
All,
diff --git a/AppInspector.RulesEngine/PatternType.cs b/AppInspector.RulesEngine/PatternType.cs
index d4280500..8739bd00 100644
--- a/AppInspector.RulesEngine/PatternType.cs
+++ b/AppInspector.RulesEngine/PatternType.cs
@@ -1,14 +1,13 @@
// Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License.
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.RulesEngine;
///
/// Pattern Type for search pattern
///
-[JsonConverter(typeof(StringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum PatternType
{
Regex,
diff --git a/AppInspector.RulesEngine/Rule.cs b/AppInspector.RulesEngine/Rule.cs
index cb13591a..0b95091b 100644
--- a/AppInspector.RulesEngine/Rule.cs
+++ b/AppInspector.RulesEngine/Rule.cs
@@ -4,9 +4,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
namespace Microsoft.ApplicationInspector.RulesEngine;
@@ -39,20 +38,20 @@ public class Rule
[JsonIgnore]
public bool Disabled { get; set; }
- [JsonProperty(PropertyName = "name")] public string Name { get; set; } = "";
+ [JsonPropertyName("name")] public string Name { get; set; } = "";
- [JsonProperty(PropertyName = "id")] public string Id { get; set; } = "";
+ [JsonPropertyName("id")] public string Id { get; set; } = "";
- [JsonProperty(PropertyName = "description")]
+ [JsonPropertyName("description")]
public string? Description { get; set; } = "";
- [JsonProperty(PropertyName = "does_not_apply_to")]
+ [JsonPropertyName("does_not_apply_to")]
public List? DoesNotApplyTo { get; set; }
- [JsonProperty(PropertyName = "applies_to")]
+ [JsonPropertyName("applies_to")]
public string[]? AppliesTo { get; set; }
- [JsonProperty(PropertyName = "applies_to_file_regex")]
+ [JsonPropertyName("applies_to_file_regex")]
public string[]? FileRegexes
{
get => _fileRegexes;
@@ -78,24 +77,24 @@ public IEnumerable CompiledFileRegexes
}
}
- [JsonProperty(PropertyName = "tags")] public string[]? Tags { get; set; }
+ [JsonPropertyName("tags")] public string[]? Tags { get; set; }
- [JsonProperty(PropertyName = "severity")]
- [JsonConverter(typeof(StringEnumConverter))]
+ [JsonPropertyName("severity")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
public Severity Severity { get; set; } = Severity.Moderate;
- [JsonProperty(PropertyName = "overrides")]
+ [JsonPropertyName("overrides")]
public string[]? Overrides { get; set; }
- [JsonProperty(PropertyName = "patterns")]
+ [JsonPropertyName("patterns")]
public SearchPattern[] Patterns { get; set; } = Array.Empty();
- [JsonProperty(PropertyName = "conditions")]
+ [JsonPropertyName("conditions")]
public SearchCondition[]? Conditions { get; set; }
- [JsonProperty(PropertyName = "must-match")]
+ [JsonPropertyName("must-match")]
public string[]? MustMatch { get; set; }
- [JsonProperty(PropertyName = "must-not-match")]
+ [JsonPropertyName("must-not-match")]
public string[]? MustNotMatch { get; set; }
}
\ No newline at end of file
diff --git a/AppInspector.RulesEngine/SearchCondition.cs b/AppInspector.RulesEngine/SearchCondition.cs
index ca8b0737..63e7be14 100644
--- a/AppInspector.RulesEngine/SearchCondition.cs
+++ b/AppInspector.RulesEngine/SearchCondition.cs
@@ -1,17 +1,17 @@
// Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License.
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.RulesEngine;
public class SearchCondition
{
- [JsonProperty(PropertyName = "negate_finding")]
+ [JsonPropertyName("negate_finding")]
public bool NegateFinding { get; set; }
- [JsonProperty(PropertyName = "pattern")]
+ [JsonPropertyName("pattern")]
public SearchPattern? Pattern { get; set; }
- [JsonProperty(PropertyName = "search_in")]
+ [JsonPropertyName("search_in")]
public string? SearchIn { get; set; }
}
\ No newline at end of file
diff --git a/AppInspector.RulesEngine/SearchPattern.cs b/AppInspector.RulesEngine/SearchPattern.cs
index 5c3ac882..898371db 100644
--- a/AppInspector.RulesEngine/SearchPattern.cs
+++ b/AppInspector.RulesEngine/SearchPattern.cs
@@ -1,9 +1,6 @@
// Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License.
-using System.Collections.Generic;
-using System.Text.RegularExpressions;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.RulesEngine;
@@ -12,41 +9,41 @@ namespace Microsoft.ApplicationInspector.RulesEngine;
///
public class SearchPattern
{
- [JsonProperty(PropertyName = "confidence")]
- [JsonConverter(typeof(StringEnumConverter))]
+ [JsonPropertyName("confidence")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
public Confidence Confidence { get; set; }
- [JsonProperty(PropertyName = "modifiers")]
+ [JsonPropertyName("modifiers")]
public string[]? Modifiers { get; set; }
- [JsonProperty(PropertyName = "pattern")]
+ [JsonPropertyName("pattern")]
public string? Pattern { get; set; }
- [JsonProperty(PropertyName = "type")]
- [JsonConverter(typeof(StringEnumConverter))]
+ [JsonPropertyName("type")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
public PatternType? PatternType { get; set; }
- [JsonProperty(PropertyName = "scopes")]
+ [JsonPropertyName("scopes")]
public PatternScope[]? Scopes { get; set; }
///
/// If set, attempt to parse the file as XML and if that is possible,
/// before running the pattern, select down to the XPath provided
///
- [JsonProperty(PropertyName = "xpaths")]
+ [JsonPropertyName("xpaths")]
public string[]? XPaths { get; set; }
///
/// If set, attempt to parse the file as JSON and if that is possible,
/// before running the pattern, select down to the JsonPath provided
///
- [JsonProperty(PropertyName = "jsonpaths")]
+ [JsonPropertyName("jsonpaths")]
public string[]? JsonPaths { get; set; }
///
/// If set, attempt to parse the file as YML and if that is possible,
/// before running the pattern, select down to the JsonPath provided
///
- [JsonProperty(PropertyName = "ymlpaths")]
+ [JsonPropertyName("ymlpaths")]
public string[]? YamlPaths { get; set; }
}
\ No newline at end of file
diff --git a/AppInspector.RulesEngine/Severity.cs b/AppInspector.RulesEngine/Severity.cs
index b008398b..c1ecc81e 100644
--- a/AppInspector.RulesEngine/Severity.cs
+++ b/AppInspector.RulesEngine/Severity.cs
@@ -1,8 +1,7 @@
// Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License.
using System;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.RulesEngine;
@@ -10,7 +9,7 @@ namespace Microsoft.ApplicationInspector.RulesEngine;
/// Issue severity
///
[Flags]
-[JsonConverter(typeof(StringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Severity
{
///
diff --git a/AppInspector.RulesEngine/TypedRuleSet.cs b/AppInspector.RulesEngine/TypedRuleSet.cs
index da4712ae..a61ba47a 100644
--- a/AppInspector.RulesEngine/TypedRuleSet.cs
+++ b/AppInspector.RulesEngine/TypedRuleSet.cs
@@ -2,9 +2,9 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
+using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
namespace Microsoft.ApplicationInspector.RulesEngine;
@@ -62,10 +62,6 @@ private IEnumerable AppInspectorRulesAsEnumerableT()
/// Tag for the rules
/// Thrown if the filename is null or empty
/// Thrown if the specified file cannot be found on the file system
- ///
- /// Thrown if the specified file cannot be deserialized as a
- ///
- ///
public void AddPath(string path, string? tag = null)
{
if (Directory.Exists(path))
@@ -88,11 +84,7 @@ public void AddPath(string path, string? tag = null)
/// Path to rules folder
/// Tag for the rules
/// Thrown if the filename is null or empty
- /// Thrown if the specified file cannot be found on the file system
- ///
- /// Thrown if the specified file cannot be deserialized as a
- ///
- ///
+ /// Thrown if the specified file cannot be found on the file system
public void AddDirectory(string path, string? tag = null)
{
if (!Directory.Exists(path))
@@ -111,10 +103,6 @@ public void AddDirectory(string path, string? tag = null)
/// Tag for the rules
/// Thrown if the filename is null or empty
/// Thrown if the specified file cannot be found on the file system
- ///
- /// Thrown if the specified file cannot be deserialized as a
- ///
- ///
public void AddFile(string? filename, string? tag = null)
{
if (string.IsNullOrEmpty(filename))
@@ -180,18 +168,27 @@ public void AddRule(T rule)
///
///
/// Add an additional tag to the rules when added.
+ ///
+ /// Thrown if the specified json string cannot be deserialized as a
+ ///
+ ///
///
internal IEnumerable StringToRules(string jsonString, string sourceName, string? tag = null)
{
- List? ruleList = null;
+ List? ruleList;
try
{
- ruleList = JsonConvert.DeserializeObject>(jsonString);
+ var options = new JsonSerializerOptions()
+ {
+ AllowTrailingCommas = true,
+
+ };
+ ruleList = JsonSerializer.Deserialize>(jsonString, options);
}
- catch (JsonSerializationException jsonSerializationException)
+ catch (JsonException jsonSerializationException)
{
_logger.LogError("Failed to deserialize '{0}' at Line {1} Column {2}", sourceName,
- jsonSerializationException.LineNumber, jsonSerializationException.LinePosition);
+ jsonSerializationException.LineNumber, jsonSerializationException.BytePositionInLine);
throw;
}
diff --git a/AppInspector.Tests/AppInspector.Tests.csproj b/AppInspector.Tests/AppInspector.Tests.csproj
index 79872bbf..9bcc708f 100644
--- a/AppInspector.Tests/AppInspector.Tests.csproj
+++ b/AppInspector.Tests/AppInspector.Tests.csproj
@@ -28,6 +28,7 @@
+
diff --git a/AppInspector.Tests/Languages/LanguagesTests.cs b/AppInspector.Tests/Languages/LanguagesTests.cs
index d7c8332d..219dba07 100644
--- a/AppInspector.Tests/Languages/LanguagesTests.cs
+++ b/AppInspector.Tests/Languages/LanguagesTests.cs
@@ -1,11 +1,11 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Text.Json;
using Microsoft.ApplicationInspector.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Newtonsoft.Json;
using Serilog.Events;
namespace AppInspector.Tests.Languages;
@@ -88,10 +88,10 @@ public void DetectCustomLanguage()
[TestMethod]
public void EmptyLanguagesOnInvalidCommentsAndLanguages()
{
- Assert.ThrowsException(() =>
+ Assert.ThrowsException(() =>
Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory,
invalidTestCommentsPath));
- Assert.ThrowsException(() =>
+ Assert.ThrowsException(() =>
Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory, null,
invalidTestLanguagesPath));
}
diff --git a/AppInspector.sln b/AppInspector.sln
index ccbcd510..668e9517 100644
--- a/AppInspector.sln
+++ b/AppInspector.sln
@@ -15,7 +15,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppInspector.CLI", "AppInsp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppInspector.Tests", "AppInspector.Tests\AppInspector.Tests.csproj", "{181BD826-A428-41D9-8BEC-0D8EB2288DF5}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{F031887C-EA60-4390-9940-765E99E69B8F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppInspector.Benchmarks", "AppInspector.Benchmarks\AppInspector.Benchmarks.csproj", "{F031887C-EA60-4390-9940-765E99E69B8F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppInspector.Common", "AppInspector.Common\AppInspector.Common.csproj", "{B15415B6-6EC8-4AA1-B8AF-DF0ABE5F16DB}"
EndProject
diff --git a/AppInspector/AppInspector.Commands.csproj b/AppInspector/AppInspector.Commands.csproj
index ada00039..5ab338c2 100644
--- a/AppInspector/AppInspector.Commands.csproj
+++ b/AppInspector/AppInspector.Commands.csproj
@@ -57,7 +57,6 @@
-
diff --git a/AppInspector/Commands/AnalyzeCommand.cs b/AppInspector/Commands/AnalyzeCommand.cs
index 1615e2c7..f4e53dcf 100644
--- a/AppInspector/Commands/AnalyzeCommand.cs
+++ b/AppInspector/Commands/AnalyzeCommand.cs
@@ -16,7 +16,7 @@
using Microsoft.CST.RecursiveExtractor;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using ShellProgressBar;
namespace Microsoft.ApplicationInspector.Commands;
@@ -123,13 +123,15 @@ public AnalyzeResult()
Metadata = new MetaData("", ""); //needed for serialization for other commands; replaced later
}
- [JsonProperty(Order = 2, PropertyName = "resultCode")]
+ [JsonPropertyName("resultCode")]
+ // [JsonPropertyOrder(2)] .NET 6.0 only
public ExitCode ResultCode { get; set; }
///
/// Analyze command result object containing scan properties
///
- [JsonProperty(Order = 3, PropertyName = "metaData")]
+ [JsonPropertyName("metaData")]
+ // [JsonPropertyOrder(3)]
public MetaData Metadata { get; set; }
}
diff --git a/AppInspector/Commands/ExportTagsCommand.cs b/AppInspector/Commands/ExportTagsCommand.cs
index fa6bb735..9b03e974 100644
--- a/AppInspector/Commands/ExportTagsCommand.cs
+++ b/AppInspector/Commands/ExportTagsCommand.cs
@@ -9,7 +9,7 @@
using Microsoft.ApplicationInspector.RulesEngine;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.Commands;
@@ -39,13 +39,13 @@ public ExportTagsResult()
TagsList = new List();
}
- [JsonProperty(Order = 2, PropertyName = "resultCode")]
+ [JsonPropertyName("resultCode")]
public ExitCode ResultCode { get; set; }
///
/// List of tags exported from specified ruleset
///
- [JsonProperty(Order = 3, PropertyName = "tagsList")]
+ [JsonPropertyName("tagsList")]
public List TagsList { get; set; }
}
diff --git a/AppInspector/Commands/PackRulesCommand.cs b/AppInspector/Commands/PackRulesCommand.cs
index 5a19711c..effc0be6 100644
--- a/AppInspector/Commands/PackRulesCommand.cs
+++ b/AppInspector/Commands/PackRulesCommand.cs
@@ -3,11 +3,11 @@
using System.Collections.Generic;
using System.Linq;
+using System.Text.Json.Serialization;
using Microsoft.ApplicationInspector.Common;
using Microsoft.ApplicationInspector.RulesEngine;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
namespace Microsoft.ApplicationInspector.Commands;
@@ -31,12 +31,13 @@ public enum ExitCode
CriticalError = Utils.ExitCode.CriticalError //ensure common value for final exit log mention
}
- [JsonProperty(Order = 2)] public ExitCode ResultCode { get; set; }
+ [JsonPropertyName("resultCode")] // Order 2
+ public ExitCode ResultCode { get; set; }
///
/// List of Rules to pack as specified in pack command
///
- [JsonProperty(Order = 3)]
+ [JsonPropertyName("rules")] // Order 3
public List? Rules { get; set; }
}
diff --git a/AppInspector/Commands/TagDiffCommand.cs b/AppInspector/Commands/TagDiffCommand.cs
index 42719788..8ac67495 100644
--- a/AppInspector/Commands/TagDiffCommand.cs
+++ b/AppInspector/Commands/TagDiffCommand.cs
@@ -8,7 +8,7 @@
using Microsoft.ApplicationInspector.RulesEngine;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.Commands;
@@ -58,13 +58,13 @@ public enum DiffSource
///
/// Tag value from rule used in comparison
///
- [JsonProperty(PropertyName = "tag")]
+ [JsonPropertyName("tag")]
public string? Tag { get; set; }
///
/// Source file (src1/src2) from the command option arguments
///
- [JsonProperty(PropertyName = "source")]
+ [JsonPropertyName("source")]
public DiffSource Source { get; set; }
}
@@ -83,7 +83,7 @@ public enum ExitCode
///
/// List of tags which differ between src1 and src2
///
- [JsonProperty(Order = 3, PropertyName = "tagDiffList")]
+ [JsonPropertyName("tagDiffList")]
public List TagDiffList;
public TagDiffResult()
@@ -91,7 +91,7 @@ public TagDiffResult()
TagDiffList = new List();
}
- [JsonProperty(Order = 2, PropertyName = "resultCode")]
+ [JsonPropertyName("resultCode")] // Order 2
public ExitCode ResultCode { get; set; }
}
diff --git a/AppInspector/Commands/VerifyRulesCommand.cs b/AppInspector/Commands/VerifyRulesCommand.cs
index 7e48b321..f3a0a958 100644
--- a/AppInspector/Commands/VerifyRulesCommand.cs
+++ b/AppInspector/Commands/VerifyRulesCommand.cs
@@ -9,8 +9,8 @@
using Microsoft.ApplicationInspector.RulesEngine.OatExtensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
+using System.Text.Json.Serialization;
+using System.Text.Json;
namespace Microsoft.ApplicationInspector.Commands;
@@ -27,7 +27,7 @@ public class VerifyRulesOptions
public class VerifyRulesResult : Result
{
- [JsonConverter(typeof(StringEnumConverter))]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
public enum ExitCode
{
Verified = 0,
@@ -40,10 +40,10 @@ public VerifyRulesResult()
RuleStatusList = new List();
}
- [JsonProperty(PropertyName = "resultCode")]
+ [JsonPropertyName("resultCode")]
public ExitCode ResultCode { get; set; }
- [JsonProperty(PropertyName = "ruleStatusList")]
+ [JsonPropertyName("ruleStatusList")]
public List RuleStatusList { get; set; }
[JsonIgnore] public IEnumerable Unverified => RuleStatusList.Where(x => !x.Verified);
diff --git a/AppInspector/FileRecord.cs b/AppInspector/FileRecord.cs
index a17ee908..50f9f5cb 100644
--- a/AppInspector/FileRecord.cs
+++ b/AppInspector/FileRecord.cs
@@ -1,6 +1,5 @@
using System;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.Commands;
@@ -15,7 +14,7 @@ public class FileRecord
public DateTime AccessTime { get; set; } = DateTime.MinValue;
}
-[JsonConverter(typeof(StringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ScanState
{
None,
diff --git a/AppInspector/MetaData.cs b/AppInspector/MetaData.cs
index 2fb9c74e..c5201a9b 100644
--- a/AppInspector/MetaData.cs
+++ b/AppInspector/MetaData.cs
@@ -5,7 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.ApplicationInspector.RulesEngine;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.Commands;
@@ -24,37 +24,37 @@ public MetaData(string applicationName, string sourcePath)
///
/// Detected or derived project name
///
- [JsonProperty(PropertyName = "applicationName")]
+ [JsonPropertyName("applicationName")]
public string? ApplicationName { get; set; }
///
/// Source path provided argument
///
- [JsonProperty(PropertyName = "sourcePath")]
+ [JsonPropertyName("sourcePath")]
public string? SourcePath { get; set; }
///
/// Detected project source version
///
- [JsonProperty(PropertyName = "sourceVersion")]
+ [JsonPropertyName("sourceVersion")]
public string? SourceVersion { get; set; }
///
/// Detected source authors
///
- [JsonProperty(PropertyName = "authors")]
+ [JsonPropertyName("authors")]
public string? Authors { get; set; }
///
/// Detected source description
///
- [JsonProperty(PropertyName = "description")]
+ [JsonPropertyName("description")]
public string? Description { get; set; }
///
/// Last modified date for source code scanned
///
- [JsonProperty(PropertyName = "lastUpdated")]
+ [JsonPropertyName("lastUpdated")]
public string LastUpdated
{
get
@@ -71,27 +71,27 @@ public string LastUpdated
///
/// Date of analyze scan
///
- [JsonProperty(PropertyName = "dateScanned")]
+ [JsonPropertyName("dateScanned")]
public string? DateScanned { get; set; }
///
/// True if the overall analysis timed out
///
- [JsonProperty(PropertyName = "timedOut")]
+ [JsonPropertyName("timedOut")]
public bool TimedOut { get; set; }
//stats
///
/// Total number of files in source path
///
- [JsonProperty(PropertyName = "totalFiles")]
+ [JsonPropertyName("totalFiles")]
public int TotalFiles => Files.Count;
///
/// Total number of files Timed out on an individual timeout
///
- [JsonProperty(PropertyName = "filesTimedOut")]
+ [JsonPropertyName("filesTimedOut")]
public int FilesTimedOut
{
get { return Files.Count(x => x.Status == ScanState.TimedOut); }
@@ -100,7 +100,7 @@ public int FilesTimedOut
///
/// Total number of files scanned
///
- [JsonProperty(PropertyName = "filesAnalyzed")]
+ [JsonPropertyName("filesAnalyzed")]
public int FilesAnalyzed
{
get { return Files.Count(x => x.Status is ScanState.Analyzed or ScanState.Affected); }
@@ -109,7 +109,7 @@ public int FilesAnalyzed
///
/// Total number of skipped files based on supported formats
///
- [JsonProperty(PropertyName = "filesSkipped")]
+ [JsonPropertyName("filesSkipped")]
public int FilesSkipped
{
get { return Files.Count(x => x.Status == ScanState.Skipped); }
@@ -118,7 +118,7 @@ public int FilesSkipped
///
/// Total number of skipped files based on overall timeout
///
- [JsonProperty(PropertyName = "filesTimeOutSkipped")]
+ [JsonPropertyName("filesTimeOutSkipped")]
public int FilesTimeOutSkipped
{
get { return Files.Count(x => x.Status == ScanState.TimeOutSkipped); }
@@ -127,7 +127,7 @@ public int FilesTimeOutSkipped
///
/// Total files with at least one result
///
- [JsonProperty(PropertyName = "filesAffected")]
+ [JsonPropertyName("filesAffected")]
public int FilesAffected
{
get { return Files.Count(x => x.Status == ScanState.Affected); }
@@ -136,7 +136,7 @@ public int FilesAffected
///
/// Number of files which encountered an error when processing other than timing out.
///
- [JsonProperty(PropertyName = "filesErrored")]
+ [JsonPropertyName("filesErrored")]
public int FileErrored
{
get { return Files.Count(x => x.Status == ScanState.Error); }
@@ -145,13 +145,13 @@ public int FileErrored
///
/// Total matches with supplied argument settings
///
- [JsonProperty(PropertyName = "totalMatchesCount")]
+ [JsonPropertyName("totalMatchesCount")]
public int TotalMatchesCount => Matches?.Count ?? 0;
///
/// Total unique matches by Rule Id
///
- [JsonProperty(PropertyName = "uniqueMatchesCount")]
+ [JsonPropertyName("uniqueMatchesCount")]
public int UniqueMatchesCount
{
get { return Matches?.Select(x => x.RuleId).Distinct().Count() ?? 0; }
@@ -160,81 +160,81 @@ public int UniqueMatchesCount
///
/// List of detected package types
///
- [JsonProperty(PropertyName = "packageTypes")]
+ [JsonPropertyName("packageTypes")]
public List? PackageTypes { get; set; } = new();
///
/// List of detected application types
///
- [JsonProperty(PropertyName = "appTypes")]
+ [JsonPropertyName("appTypes")]
public List? AppTypes { get; set; } = new();
///
/// List of detected unique tags
///
- [JsonProperty(PropertyName = "uniqueTags")]
+ [JsonPropertyName("uniqueTags")]
public List UniqueTags { get; set; } = new();
///
/// List of detected unique code dependency includes
///
- [JsonProperty(PropertyName = "uniqueDependencies")]
+ [JsonPropertyName("uniqueDependencies")]
public List? UniqueDependencies { get; set; } = new();
///
/// List of detected output types
///
- [JsonProperty(PropertyName = "outputs")]
+ [JsonPropertyName("outputs")]
public List? Outputs { get; set; } = new();
///
/// List of detected target types
///
- [JsonProperty(PropertyName = "targets")]
+ [JsonPropertyName("targets")]
public List Targets { get; set; } = new();
///
/// List of detected OS targets
///
- [JsonProperty(PropertyName = "OSTargets")]
+ [JsonPropertyName("OSTargets")]
public List? OSTargets { get; set; } = new();
///
/// LIst of detected file types (extension based)
///
- [JsonProperty(PropertyName = "fileExtensions")]
+ [JsonPropertyName("fileExtensions")]
public List? FileExtensions { get; set; } = new();
///
/// List of detected cloud host targets
///
- [JsonProperty(PropertyName = "cloudTargets")]
+ [JsonPropertyName("cloudTargets")]
public List? CloudTargets { get; set; } = new();
///
/// List of detected cpu targets
///
- [JsonProperty(PropertyName = "CPUTargets")]
+ [JsonPropertyName("CPUTargets")]
public List? CPUTargets { get; set; } = new();
///
/// List of detected programming languages used and count of files
///
- [JsonProperty(PropertyName = "languages")]
+ [JsonPropertyName("languages")]
public IDictionary? Languages { get; set; } //unable to init here for constr arg
///
/// List of detected tag counters i.e. metrics
///
- [JsonProperty(PropertyName = "tagCounters")]
+ [JsonPropertyName("tagCounters")]
public List? TagCounters { get; set; } = new();
///
/// List of detailed MatchRecords from scan
///
- [JsonProperty(PropertyName = "detailedMatchList")]
+ [JsonPropertyName("detailedMatchList")]
public List Matches { get; set; } = new();
- [JsonProperty(PropertyName = "filesInformation")]
+ [JsonPropertyName("filesInformation")]
public List Files { get; set; } = new();
}
\ No newline at end of file
diff --git a/AppInspector/MetaDataHelper.cs b/AppInspector/MetaDataHelper.cs
index 19ef1bf7..a72b3d5f 100644
--- a/AppInspector/MetaDataHelper.cs
+++ b/AppInspector/MetaDataHelper.cs
@@ -6,7 +6,9 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Runtime.CompilerServices;
using Microsoft.ApplicationInspector.RulesEngine;
+[assembly:InternalsVisibleTo("AppInspector.Benchmarks")]
namespace Microsoft.ApplicationInspector.Commands;
@@ -151,7 +153,6 @@ public void AddMatchRecord(MatchRecord matchRecord)
}
}
-
///
/// Transfer concurrent data from scan to analyze result with sorted, simplier types for callers
///
diff --git a/AppInspector/MetricTagCounter.cs b/AppInspector/MetricTagCounter.cs
index 0454ca8a..d420a132 100644
--- a/AppInspector/MetricTagCounter.cs
+++ b/AppInspector/MetricTagCounter.cs
@@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System.Text.Json.Serialization;
using System.Threading;
-using Newtonsoft.Json;
namespace Microsoft.ApplicationInspector.Commands;
@@ -14,9 +14,11 @@ public class MetricTagCounter
{
private int _count;
- [JsonProperty(PropertyName = "tag")] public string? Tag { get; set; }
+ [JsonPropertyName("tag")]
+ public string? Tag { get; set; }
- [JsonProperty(PropertyName = "count")] public int Count => _count;
+ [JsonPropertyName("count")]
+ public int Count => _count;
internal void IncrementCount(int amount = 1)
{
diff --git a/AppInspector/Result.cs b/AppInspector/Result.cs
index 9fce18c2..13c037e9 100644
--- a/AppInspector/Result.cs
+++ b/AppInspector/Result.cs
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Microsoft.ApplicationInspector.Commands;
@@ -10,6 +10,6 @@ namespace Microsoft.ApplicationInspector.Commands;
///
public class Result
{
- [JsonProperty(Order = 1, PropertyName = "appVersion")]
+ [JsonPropertyName("appVersion")] // Order 1
public string? AppVersion { get; set; }
}
\ No newline at end of file
diff --git a/version.json b/version.json
index e2f55d4e..5b110db6 100644
--- a/version.json
+++ b/version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
- "version": "1.6",
+ "version": "1.7",
"publicReleaseRefSpec": [
"^refs/heads/main$",
"^refs/heads/v\\d+(?:\\.\\d+)?$"