diff --git a/changelog/pending/20221206--sdk-dotnet--add-output-jsonserialize-using-system-text-json.yaml b/changelog/pending/20221206--sdk-dotnet--add-output-jsonserialize-using-system-text-json.yaml new file mode 100644 index 000000000000..c4047b5713fb --- /dev/null +++ b/changelog/pending/20221206--sdk-dotnet--add-output-jsonserialize-using-system-text-json.yaml @@ -0,0 +1,4 @@ +changes: +- type: feat + scope: sdk/dotnet + description: Add Output.JsonSerialize using System.Text.Json. diff --git a/sdk/dotnet/Pulumi.Tests/Core/OutputTests.cs b/sdk/dotnet/Pulumi.Tests/Core/OutputTests.cs index cba9f7cefe72..e178ebedcc1f 100644 --- a/sdk/dotnet/Pulumi.Tests/Core/OutputTests.cs +++ b/sdk/dotnet/Pulumi.Tests/Core/OutputTests.cs @@ -9,6 +9,20 @@ namespace Pulumi.Tests.Core { + // Simple struct used for JSON tests + public struct TestStructure { + public int X { get; set;} + + private int y; + + public string Z => (y+1).ToString(); + + public TestStructure(int x, int y) { + X = x; + this.y = y; + } + } + public class OutputTests : PulumiTest { private static Output CreateOutput(T value, bool isKnown, bool isSecret = false) @@ -618,6 +632,45 @@ public Task CreateSecretSetsSecret() Assert.True(data.IsSecret); Assert.Equal(0, data.Value); }); + + [Fact] + public Task JsonSerializeBasic() + => RunInNormal(async () => + { + var o1 = CreateOutput(0, true); + var o2 = Output.JsonSerialize(o1); + var data = await o2.DataTask.ConfigureAwait(false); + Assert.True(data.IsKnown); + Assert.False(data.IsSecret); + Assert.Equal("0", data.Value); + }); + + [Fact] + public Task JsonSerializeWithOptions() + => RunInNormal(async () => + { + var v = new System.Collections.Generic.Dictionary(); + v.Add("a", new TestStructure(1, 2)); + v.Add("b", new TestStructure(int.MinValue, int.MaxValue)); + var o1 = CreateOutput(v, true); + var options = new System.Text.Json.JsonSerializerOptions(); + options.WriteIndented = true; + var o2 = Output.JsonSerialize(o1, options); + var data = await o2.DataTask.ConfigureAwait(false); + Assert.True(data.IsKnown); + Assert.False(data.IsSecret); + var expected = @"{ + ""a"": { + ""X"": 1, + ""Z"": ""3"" + }, + ""b"": { + ""X"": -2147483648, + ""Z"": ""-2147483648"" + } +}"; + Assert.Equal(expected, data.Value); + }); } } } diff --git a/sdk/dotnet/Pulumi/Core/Output.cs b/sdk/dotnet/Pulumi/Core/Output.cs index 4523a7faa60f..114b70ff0819 100644 --- a/sdk/dotnet/Pulumi/Core/Output.cs +++ b/sdk/dotnet/Pulumi/Core/Output.cs @@ -106,6 +106,18 @@ public static Output Format(FormattableString formattableString) internal static Output> Concat(Output> values1, Output> values2) => Tuple(values1, values2).Apply(tuple => tuple.Item1.AddRange(tuple.Item2)); + + /// + /// Uses to serialize the given value into a JSON string. + /// + public static Output JsonSerialize(Output value, System.Text.Json.JsonSerializerOptions? options = null) { + return value.Apply(async v => { + var utf8 = new System.IO.MemoryStream(); + await System.Text.Json.JsonSerializer.SerializeAsync(utf8, v, options); + return System.Text.Encoding.UTF8.GetString(new ReadOnlySpan(utf8.GetBuffer(), 0, (int)utf8.Length)); + }); + } } /// @@ -128,7 +140,7 @@ internal interface IOutput /// s are a key part of how Pulumi tracks dependencies between s. Because the values of outputs are not available until resources are /// created, these are represented using the special s type, which - /// internally represents two things: an eventually available value of the output and + /// internally represents two things: an eventually available value of the output and /// the dependency on the source(s) of the output value. /// In fact, s is quite similar to . /// Additionally, they carry along dependency information.