Skip to content

Commit 6d2ff62

Browse files
dietervarturcic
authored andcommitted
deterministically serialize number and string JSON output
Ever since the introduction of the JsonOutputFormatter way back in 2014 [1] all fields where serialized as JSON string. Except for values looking like an int which where then serialized as JSON numbers. This had some strange unpredictable side effects like PreReleaseNumber being an empty JSON string instead of null or a "2009069" ShortSha being formatted as a JSON number. This change ensures all fields are serialized as JSON string except Major, Minor, Patch, PreReleaseNumber, WeightedPreReleaseNumber, CommitsSinceVersionSource and UncommittedChanges which are now always serialized as JSON number. Deserialisation remains the same as before for all fields so we continue to accept both JSON string and JSON number formatted values. Fixes #1688 and fixes #2304. [1] f2daf60#diff-fde1a8ff593c6ac2ad558a6f9bb512e0350db91343b185f9b2a00d1d6e848bc3
1 parent b10adbb commit 6d2ff62

10 files changed

+190
-50
lines changed

src/GitVersion.Core.Tests/VersionCalculation/Approved/VariableProviderTests.ProvidesVariablesInContinuousDeliveryModeForFeatureBranch.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"PreReleaseTagWithDash": "",
77
"PreReleaseLabel": "",
88
"PreReleaseLabelWithDash": "",
9-
"PreReleaseNumber": "",
9+
"PreReleaseNumber": null,
1010
"WeightedPreReleaseNumber": 0,
1111
"BuildMetaData": 5,
1212
"BuildMetaDataPadded": "0005",

src/GitVersion.Core.Tests/VersionCalculation/Approved/VariableProviderTests.ProvidesVariablesInContinuousDeliveryModeForFeatureBranchWithCustomAssemblyInfoFormat.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"PreReleaseTagWithDash": "",
77
"PreReleaseLabel": "",
88
"PreReleaseLabelWithDash": "",
9-
"PreReleaseNumber": "",
9+
"PreReleaseNumber": null,
1010
"WeightedPreReleaseNumber": 0,
1111
"BuildMetaData": 5,
1212
"BuildMetaDataPadded": "0005",

src/GitVersion.Core.Tests/VersionCalculation/Approved/VariableProviderTests.ProvidesVariablesInContinuousDeliveryModeForStable.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"PreReleaseTagWithDash": "",
77
"PreReleaseLabel": "",
88
"PreReleaseLabelWithDash": "",
9-
"PreReleaseNumber": "",
9+
"PreReleaseNumber": null,
1010
"WeightedPreReleaseNumber": 0,
1111
"BuildMetaData": 5,
1212
"BuildMetaDataPadded": "0005",

src/GitVersion.Core.Tests/VersionCalculation/Approved/VariableProviderTests.ProvidesVariablesInContinuousDeploymentModeForPreRelease.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"PreReleaseLabelWithDash": "-unstable",
99
"PreReleaseNumber": 8,
1010
"WeightedPreReleaseNumber": 8,
11-
"BuildMetaData": "",
11+
"BuildMetaData": null,
1212
"BuildMetaDataPadded": "",
1313
"FullBuildMetaData": "Branch.develop.Sha.commitSha",
1414
"MajorMinorPatch": "1.2.3",

src/GitVersion.Core.Tests/VersionCalculation/Approved/VariableProviderTests.ProvidesVariablesInContinuousDeploymentModeForStable.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"PreReleaseLabelWithDash": "-ci",
99
"PreReleaseNumber": 5,
1010
"WeightedPreReleaseNumber": 5,
11-
"BuildMetaData": "",
11+
"BuildMetaData": null,
1212
"BuildMetaDataPadded": "",
1313
"FullBuildMetaData": "Branch.develop.Sha.commitSha",
1414
"MajorMinorPatch": "1.2.3",

src/GitVersion.Core.Tests/VersionCalculation/Approved/VariableProviderTests.ProvidesVariablesInContinuousDeploymentModeForStableWhenCurrentCommitIsTagged.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"PreReleaseTagWithDash": "",
77
"PreReleaseLabel": "",
88
"PreReleaseLabelWithDash": "",
9-
"PreReleaseNumber": "",
9+
"PreReleaseNumber": null,
1010
"WeightedPreReleaseNumber": 0,
1111
"BuildMetaData": 5,
1212
"BuildMetaDataPadded": "0005",

src/GitVersion.Core/Model/VersionVariables.cs

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
using System;
22
using System.Buffers;
3-
using System.Buffers.Text;
43
using System.Collections;
54
using System.Collections.Generic;
65
using System.IO;
76
using System.Linq;
87
using System.Text.Encodings.Web;
98
using System.Text.Json;
10-
using System.Text.Json.Serialization;
11-
using JetBrains.Annotations;
129
using YamlDotNet.Serialization;
1310
using static GitVersion.Extensions.ObjectExtensions;
1411

@@ -116,8 +113,8 @@ public VersionVariables(string major,
116113
public string VersionSourceSha { get; }
117114
public string CommitsSinceVersionSource { get; }
118115
public string CommitsSinceVersionSourcePadded { get; }
119-
120116
public string UncommittedChanges { get; }
117+
public string CommitDate { get; set; }
121118

122119
[ReflectionIgnore]
123120
public static IEnumerable<string> AvailableVariables
@@ -132,8 +129,6 @@ public static IEnumerable<string> AvailableVariables
132129
}
133130
}
134131

135-
public string CommitDate { get; set; }
136-
137132
[ReflectionIgnore]
138133
public string FileName { get; set; }
139134

@@ -199,7 +194,14 @@ private static bool ContainsKey(string variable)
199194

200195
public override string ToString()
201196
{
202-
var variables = this.GetProperties().ToDictionary(x => x.Key, x => x.Value);
197+
var variablesType = typeof(VersionVariablesJsonModel);
198+
var variables = new VersionVariablesJsonModel();
199+
200+
foreach (KeyValuePair<string, string> property in this.GetProperties())
201+
{
202+
variablesType.GetProperty(property.Key).SetValue(variables, property.Value);
203+
}
204+
203205
var serializeOptions = JsonSerializerOptions();
204206

205207
return JsonSerializer.Serialize(variables, serializeOptions);
@@ -211,45 +213,9 @@ private static JsonSerializerOptions JsonSerializerOptions()
211213
{
212214
WriteIndented = true,
213215
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
214-
Converters = { new GitVersionStringConverter() }
216+
Converters = { new VersionVariablesJsonStringConverter() }
215217
};
216218
return serializeOptions;
217219
}
218220
}
219-
220-
public class GitVersionStringConverter : JsonConverter<string>
221-
{
222-
public override bool CanConvert(Type typeToConvert)
223-
=> typeToConvert == typeof(string);
224-
225-
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
226-
{
227-
if (reader.TokenType != JsonTokenType.Number && typeToConvert == typeof(string))
228-
return reader.GetString() ?? "";
229-
230-
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
231-
if (Utf8Parser.TryParse(span, out long number, out var bytesConsumed) && span.Length == bytesConsumed)
232-
return number.ToString();
233-
234-
var data = reader.GetString();
235-
236-
throw new InvalidOperationException($"'{data}' is not a correct expected value!")
237-
{
238-
Source = nameof(GitVersionStringConverter)
239-
};
240-
}
241-
242-
public override void Write(Utf8JsonWriter writer, [CanBeNull] string value, JsonSerializerOptions options)
243-
{
244-
value ??= string.Empty;
245-
if (NotAPaddedNumber(value) && int.TryParse(value, out var number))
246-
writer.WriteNumberValue(number);
247-
else
248-
writer.WriteStringValue(value);
249-
}
250-
251-
public override bool HandleNull => true;
252-
253-
private static bool NotAPaddedNumber(string value) => value != null && (value == "0" || !value.StartsWith("0"));
254-
}
255221
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace GitVersion.OutputVariables
4+
{
5+
public class VersionVariablesJsonModel
6+
{
7+
[JsonConverter(typeof(VersionVariablesJsonNumberConverter))]
8+
public string Major { get; set; }
9+
[JsonConverter(typeof(VersionVariablesJsonNumberConverter))]
10+
public string Minor { get; set; }
11+
[JsonConverter(typeof(VersionVariablesJsonNumberConverter))]
12+
public string Patch { get; set; }
13+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
14+
public string PreReleaseTag { get; set; }
15+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
16+
public string PreReleaseTagWithDash { get; set; }
17+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
18+
public string PreReleaseLabel { get; set; }
19+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
20+
public string PreReleaseLabelWithDash { get; set; }
21+
[JsonConverter(typeof(VersionVariablesJsonNumberConverter))]
22+
public string PreReleaseNumber { get; set; }
23+
[JsonConverter(typeof(VersionVariablesJsonNumberConverter))]
24+
public string WeightedPreReleaseNumber { get; set; }
25+
[JsonConverter(typeof(VersionVariablesJsonNumberConverter))]
26+
public string BuildMetaData { get; set; }
27+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
28+
public string BuildMetaDataPadded { get; set; }
29+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
30+
public string FullBuildMetaData { get; set; }
31+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
32+
public string MajorMinorPatch { get; set; }
33+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
34+
public string SemVer { get; set; }
35+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
36+
public string LegacySemVer { get; set; }
37+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
38+
public string LegacySemVerPadded { get; set; }
39+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
40+
public string AssemblySemVer { get; set; }
41+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
42+
public string AssemblySemFileVer { get; set; }
43+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
44+
public string FullSemVer { get; set; }
45+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
46+
public string InformationalVersion { get; set; }
47+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
48+
public string BranchName { get; set; }
49+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
50+
public string EscapedBranchName { get; set; }
51+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
52+
public string Sha { get; set; }
53+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
54+
public string ShortSha { get; set; }
55+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
56+
public string NuGetVersionV2 { get; set; }
57+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
58+
public string NuGetVersion { get; set; }
59+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
60+
public string NuGetPreReleaseTagV2 { get; set; }
61+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
62+
public string NuGetPreReleaseTag { get; set; }
63+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
64+
public string VersionSourceSha { get; set; }
65+
[JsonConverter(typeof(VersionVariablesJsonNumberConverter))]
66+
public string CommitsSinceVersionSource { get; set; }
67+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
68+
public string CommitsSinceVersionSourcePadded { get; set; }
69+
[JsonConverter(typeof(VersionVariablesJsonNumberConverter))]
70+
public string UncommittedChanges { get; set; }
71+
[JsonConverter(typeof(VersionVariablesJsonStringConverter))]
72+
public string CommitDate { get; set; }
73+
}
74+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
using System.Buffers;
3+
using System.Buffers.Text;
4+
using System.Linq;
5+
using System.Text.Json;
6+
using System.Text.Json.Serialization;
7+
using JetBrains.Annotations;
8+
9+
namespace GitVersion.OutputVariables
10+
{
11+
public class VersionVariablesJsonNumberConverter : JsonConverter<string>
12+
{
13+
public override bool CanConvert(Type typeToConvert)
14+
=> typeToConvert == typeof(string);
15+
16+
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
17+
{
18+
if (reader.TokenType != JsonTokenType.Number && typeToConvert == typeof(string))
19+
return reader.GetString() ?? "";
20+
21+
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
22+
if (Utf8Parser.TryParse(span, out long number, out var bytesConsumed) && span.Length == bytesConsumed)
23+
return number.ToString();
24+
25+
var data = reader.GetString();
26+
27+
throw new InvalidOperationException($"'{data}' is not a correct expected value!")
28+
{
29+
Source = nameof(VersionVariablesJsonNumberConverter)
30+
};
31+
}
32+
33+
public override void Write(Utf8JsonWriter writer, [CanBeNull] string value, JsonSerializerOptions options)
34+
{
35+
if (string.IsNullOrWhiteSpace(value))
36+
{
37+
writer.WriteNullValue();
38+
}
39+
else if (int.TryParse(value, out var number))
40+
{
41+
writer.WriteNumberValue(number);
42+
}
43+
else
44+
{
45+
throw new InvalidOperationException($"'{value}' is not a correct expected value!")
46+
{
47+
Source = nameof(VersionVariablesJsonStringConverter)
48+
};
49+
}
50+
51+
}
52+
53+
public override bool HandleNull => true;
54+
55+
private static bool NotAPaddedNumber(string value) => value != null && (value == "0" || !value.StartsWith("0"));
56+
}
57+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System;
2+
using System.Buffers;
3+
using System.Buffers.Text;
4+
using System.Linq;
5+
using System.Text.Json;
6+
using System.Text.Json.Serialization;
7+
using JetBrains.Annotations;
8+
9+
namespace GitVersion.OutputVariables
10+
{
11+
public class VersionVariablesJsonStringConverter : JsonConverter<string>
12+
{
13+
public override bool CanConvert(Type typeToConvert)
14+
=> typeToConvert == typeof(string);
15+
16+
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
17+
{
18+
if (reader.TokenType != JsonTokenType.Number && typeToConvert == typeof(string))
19+
return reader.GetString() ?? "";
20+
21+
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
22+
if (Utf8Parser.TryParse(span, out long number, out var bytesConsumed) && span.Length == bytesConsumed)
23+
return number.ToString();
24+
25+
var data = reader.GetString();
26+
27+
throw new InvalidOperationException($"'{data}' is not a correct expected value!")
28+
{
29+
Source = nameof(VersionVariablesJsonStringConverter)
30+
};
31+
}
32+
33+
public override void Write(Utf8JsonWriter writer, [CanBeNull] string value, JsonSerializerOptions options)
34+
{
35+
value ??= string.Empty;
36+
writer.WriteStringValue(value);
37+
}
38+
39+
public override bool HandleNull => true;
40+
41+
private static bool NotAPaddedNumber(string value) => value != null && (value == "0" || !value.StartsWith("0"));
42+
}
43+
}

0 commit comments

Comments
 (0)