Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Json parsing feedback
  • Loading branch information
tmat committed Dec 11, 2025
commit d50ff42853a2ccce42ad919e95e80e98b1f65dfd
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json.Serialization;

namespace Microsoft.DotNet.ProjectTools;

public sealed class ExecutableLaunchProfile : LaunchProfile
{
public const string WorkingDirectoryPropertyName = "workingDirectory";
public const string ExecutablePathPropertyName = "executablePath";

[JsonPropertyName("executablePath")]
public required string ExecutablePath { get; init; }

[JsonPropertyName("workingDirectory")]
public string? WorkingDirectory { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,11 @@

using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Microsoft.DotNet.ProjectTools;

internal sealed class ExecutableLaunchProfileParser : LaunchProfileParser
{
private sealed class Json
{
[JsonPropertyName("commandName")]
public string? CommandName { get; set; }

[JsonPropertyName("executablePath")]
public string? ExecutablePath { get; set; }

[JsonPropertyName("commandLineArgs")]
public string? CommandLineArgs { get; set; }

[JsonPropertyName("workingDirectory")]
public string? WorkingDirectory { get; set; }

[JsonPropertyName("dotnetRunMessages")]
public bool DotNetRunMessages { get; set; }

[JsonPropertyName("environmentVariables")]
public Dictionary<string, string>? EnvironmentVariables { get; set; }
}

public const string CommandName = "Executable";

public static readonly ExecutableLaunchProfileParser Instance = new();
Expand All @@ -40,21 +18,12 @@ private ExecutableLaunchProfileParser()

public override LaunchProfileParseResult ParseProfile(string launchSettingsPath, string? launchProfileName, string json)
{
var profile = JsonSerializer.Deserialize<Json>(json);
var profile = JsonSerializer.Deserialize<ExecutableLaunchProfile>(json);
if (profile == null)
{
return LaunchProfileParseResult.Failure(Resources.LaunchProfileIsNotAJsonObject);
}

if (profile.ExecutablePath == null)
{
return LaunchProfileParseResult.Failure(
string.Format(
Resources.LaunchProfile0IsMissingProperty1,
LaunchProfileParser.GetLaunchProfileDisplayName(launchProfileName),
ExecutableLaunchProfile.ExecutablePathPropertyName));
}

if (!TryParseWorkingDirectory(launchSettingsPath, profile.WorkingDirectory, out var workingDirectory, out var error))
{
return LaunchProfileParseResult.Failure(error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;
using System.Text.Json.Serialization;

namespace Microsoft.DotNet.ProjectTools;

public abstract class LaunchProfile
{
[JsonIgnore]
public string? LaunchProfileName { get; init; }

[JsonPropertyName("dotnetRunMessages")]
public bool DotNetRunMessages { get; init; }

[JsonPropertyName("commandLineArgs")]
public string? CommandLineArgs { get; init; }

public required ImmutableDictionary<string, string> EnvironmentVariables { get; init; }
[JsonPropertyName("environmentVariables")]
public ImmutableDictionary<string, string> EnvironmentVariables { get; init; } = [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ internal abstract class LaunchProfileParser
public static string GetLaunchProfileDisplayName(string? launchProfile)
=> string.IsNullOrEmpty(launchProfile) ? Resources.DefaultLaunchProfileDisplayName : launchProfile;

protected static ImmutableDictionary<string, string> ParseEnvironmentVariables(Dictionary<string, string>? values)
protected static ImmutableDictionary<string, string> ParseEnvironmentVariables(ImmutableDictionary<string, string> values)
{
if (values is null or { Count: 0 })
if (values.Count == 0)
{
return [];
return values;
}

var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.Ordinal);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json.Serialization;

namespace Microsoft.DotNet.ProjectTools;

public sealed class ProjectLaunchProfile : LaunchProfile
{
[JsonPropertyName("launchBrowser")]
public bool LaunchBrowser { get; init; }

[JsonPropertyName("launchUrl")]
public string? LaunchUrl { get; init; }

[JsonPropertyName("applicationUrl")]
public string? ApplicationUrl { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Microsoft.DotNet.ProjectTools;

internal sealed class ProjectLaunchProfileParser : LaunchProfileParser
{
internal sealed class Json
{
[JsonPropertyName("commandName")]
public string? CommandName { get; set; }

[JsonPropertyName("commandLineArgs")]
public string? CommandLineArgs { get; set; }

[JsonPropertyName("launchBrowser")]
public bool LaunchBrowser { get; set; }

[JsonPropertyName("launchUrl")]
public string? LaunchUrl { get; set; }

[JsonPropertyName("applicationUrl")]
public string? ApplicationUrl { get; set; }

[JsonPropertyName("dotnetRunMessages")]
public bool DotNetRunMessages { get; set; }

[JsonPropertyName("environmentVariables")]
public Dictionary<string, string>? EnvironmentVariables { get; set; }
}

public const string CommandName = "Project";

public static readonly ProjectLaunchProfileParser Instance = new();
Expand All @@ -42,7 +17,7 @@ private ProjectLaunchProfileParser()

public override LaunchProfileParseResult ParseProfile(string launchSettingsPath, string? launchProfileName, string json)
{
var profile = JsonSerializer.Deserialize<Json>(json);
var profile = JsonSerializer.Deserialize<ProjectLaunchProfile>(json);
if (profile == null)
{
return LaunchProfileParseResult.Failure(Resources.LaunchProfileIsNotAJsonObject);
Expand Down
3 changes: 0 additions & 3 deletions src/Microsoft.DotNet.ProjectTools/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,6 @@
<data name="LaunchProfilesCollectionIsNotAJsonObject" xml:space="preserve">
<value>The 'profiles' property of the launch settings document is not a JSON object.</value>
</data>
<data name="LaunchProfile0IsMissingProperty1" xml:space="preserve">
<value>Launch profile '{0}' is missing property '{1}'</value>
</data>
<data name="DuplicateCaseInsensitiveLaunchProfileNames" xml:space="preserve">
<value>There are several launch profiles with case-sensitive names, which isn't permitted:
{0}
Expand Down
5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions test/dotnet.Tests/ProjectTools/LaunchSettingsParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,11 @@ public void MissingExecutablePath()
{
var parser = ExecutableLaunchProfileParser.Instance;

var result = parser.ParseProfile("path", "Execute", """
Assert.Throws<JsonException>(() => parser.ParseProfile("path", "Execute", """
{
"commandName": "Executable"
}
""");

Assert.False(result.Successful);
Assert.Equal(string.Format(Resources.LaunchProfile0IsMissingProperty1, "Execute", "executablePath"), result.FailureReason);
"""));
}

[Theory]
Expand Down
Loading