Skip to content

Commit f096423

Browse files
committed
Migrate vcpkg and Go detectors from Newtonsoft.Json to System.Text.Json
- `VcpkgComponentDetector`: Replace `JsonConvert.DeserializeObject` with `JsonSerializer.Deserialize` - `GoCLIParser`: Replace `JsonTextReader`/`JsonSerializer` with `Utf8JsonReader` for parsing multi-object JSON output from `go list` - .NET 9 added `AllowMultipleValues`, but unfortunately we are still on .NET 8 - Add explicit `[JsonPropertyName]` attributes to vcpkg contract classes using SPDX 2.2.1 schema field names
1 parent 32c05fb commit f096423

File tree

6 files changed

+49
-14
lines changed

6 files changed

+49
-14
lines changed

src/Microsoft.ComponentDetection.Detectors/go/Parsers/GoCLIParser.cs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ namespace Microsoft.ComponentDetection.Detectors.Go;
55
using System.Collections.Generic;
66
using System.Diagnostics.CodeAnalysis;
77
using System.IO;
8+
using System.Text.Json;
89
using System.Threading.Tasks;
910
using Microsoft.ComponentDetection.Common.Telemetry.Records;
1011
using Microsoft.ComponentDetection.Contracts;
1112
using Microsoft.ComponentDetection.Contracts.TypedComponent;
1213
using Microsoft.Extensions.Logging;
13-
using Newtonsoft.Json;
1414

1515
public class GoCLIParser : IGoParser
1616
{
@@ -74,19 +74,37 @@ public async Task<bool> ParseAsync(
7474
private void RecordBuildDependencies(string goListOutput, ISingleFileComponentRecorder singleFileComponentRecorder, string projectRootDirectoryFullName)
7575
{
7676
var goBuildModules = new List<GoBuildModule>();
77-
var reader = new JsonTextReader(new StringReader(goListOutput))
78-
{
79-
SupportMultipleContent = true,
80-
};
8177

8278
using var record = new GoReplaceTelemetryRecord();
8379

84-
while (reader.Read())
80+
// Go CLI outputs multiple JSON objects (not an array), so we need to parse them one by one
81+
var remaining = goListOutput.AsMemory();
82+
while (!remaining.IsEmpty)
8583
{
86-
var serializer = new JsonSerializer();
87-
var buildModule = serializer.Deserialize<GoBuildModule>(reader);
84+
var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(remaining.ToString()));
85+
try
86+
{
87+
var buildModule = JsonSerializer.Deserialize<GoBuildModule>(ref reader);
88+
if (buildModule != null)
89+
{
90+
goBuildModules.Add(buildModule);
91+
}
92+
93+
// Move past the consumed bytes plus any whitespace
94+
var consumed = (int)reader.BytesConsumed;
95+
remaining = remaining[consumed..];
8896

89-
goBuildModules.Add(buildModule);
97+
// Skip whitespace between JSON objects
98+
while (!remaining.IsEmpty && char.IsWhiteSpace(remaining.Span[0]))
99+
{
100+
remaining = remaining[1..];
101+
}
102+
}
103+
catch (JsonException ex)
104+
{
105+
this.logger.LogWarning("Failed to parse Go build module JSON: {ErrorMessage}", ex.Message);
106+
break;
107+
}
90108
}
91109

92110
foreach (var dependency in goBuildModules)

src/Microsoft.ComponentDetection.Detectors/vcpkg/Contracts/Annotation.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@
22
namespace Microsoft.ComponentDetection.Detectors.Vcpkg.Contracts;
33

44
using System;
5+
using System.Text.Json.Serialization;
56

67
public class Annotation
78
{
9+
[JsonPropertyName("annotationDate")]
810
public DateTime Date { get; set; }
911

12+
[JsonPropertyName("comment")]
1013
public string Comment { get; set; }
1114

15+
[JsonPropertyName("annotationType")]
1216
public string Type { get; set; }
1317

18+
[JsonPropertyName("annotator")]
1419
public string Annotator { get; set; }
1520
}

src/Microsoft.ComponentDetection.Detectors/vcpkg/Contracts/ManifestInfo.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
namespace Microsoft.ComponentDetection.Detectors.Vcpkg.Contracts;
33

44
using System.Text.Json.Serialization;
5-
using Newtonsoft.Json;
65

76
public class ManifestInfo
87
{
9-
[JsonProperty("manifest-path")]
108
[JsonPropertyName("manifest-path")]
119
public string ManifestPath { get; set; }
1210
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
#nullable disable
22
namespace Microsoft.ComponentDetection.Detectors.Vcpkg.Contracts;
33

4+
using System.Text.Json.Serialization;
5+
46
public class Package
57
{
8+
[JsonPropertyName("SPDXID")]
69
public string SPDXID { get; set; }
710

11+
[JsonPropertyName("versionInfo")]
812
public string VersionInfo { get; set; }
913

14+
[JsonPropertyName("downloadLocation")]
1015
public string DownloadLocation { get; set; }
1116

17+
[JsonPropertyName("packageFileName")]
1218
public string Filename { get; set; }
1319

20+
[JsonPropertyName("homepage")]
1421
public string Homepage { get; set; }
1522

23+
[JsonPropertyName("description")]
1624
public string Description { get; set; }
1725

26+
[JsonPropertyName("name")]
1827
public string Name { get; set; }
1928

29+
[JsonPropertyName("annotations")]
2030
public Annotation[] Annotations { get; set; }
2131
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
#nullable disable
22
namespace Microsoft.ComponentDetection.Detectors.Vcpkg.Contracts;
33

4+
using System.Text.Json.Serialization;
5+
46
/// <summary>
57
/// Matches a subset of https://raw.githubusercontent.com/spdx/spdx-spec/v2.2.1/schemas/spdx-schema.json.
68
/// </summary>
79
public class VcpkgSBOM
810
{
11+
[JsonPropertyName("packages")]
912
public Package[] Packages { get; set; }
1013

14+
[JsonPropertyName("name")]
1115
public string Name { get; set; }
1216
}

src/Microsoft.ComponentDetection.Detectors/vcpkg/VcpkgComponentDetector.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ namespace Microsoft.ComponentDetection.Detectors.Vcpkg;
77
using System.IO;
88
using System.Linq;
99
using System.Reactive.Linq;
10+
using System.Text.Json;
1011
using System.Threading;
1112
using System.Threading.Tasks;
1213
using Microsoft.ComponentDetection.Contracts;
1314
using Microsoft.ComponentDetection.Contracts.Internal;
1415
using Microsoft.ComponentDetection.Contracts.TypedComponent;
1516
using Microsoft.ComponentDetection.Detectors.Vcpkg.Contracts;
1617
using Microsoft.Extensions.Logging;
17-
using Newtonsoft.Json;
1818

1919
public class VcpkgComponentDetector : FileComponentDetector
2020
{
@@ -83,7 +83,7 @@ await processRequests.ForEachAsync(async pr =>
8383
using (var reader = new StreamReader(pr.ComponentStream.Stream))
8484
{
8585
var contents = await reader.ReadToEndAsync().ConfigureAwait(false);
86-
var manifestData = JsonConvert.DeserializeObject<ManifestInfo>(contents);
86+
var manifestData = JsonSerializer.Deserialize<ManifestInfo>(contents);
8787

8888
if (manifestData == null || string.IsNullOrWhiteSpace(manifestData.ManifestPath))
8989
{
@@ -112,7 +112,7 @@ private async Task ParseSpdxFileAsync(
112112
VcpkgSBOM sbom;
113113
try
114114
{
115-
sbom = JsonConvert.DeserializeObject<VcpkgSBOM>(await reader.ReadToEndAsync());
115+
sbom = JsonSerializer.Deserialize<VcpkgSBOM>(await reader.ReadToEndAsync());
116116
}
117117
catch (Exception)
118118
{

0 commit comments

Comments
 (0)