Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New option to the JSON function to serialize unwrapped arrays #2231

Merged
merged 12 commits into from
Mar 14, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ internal sealed class EnumStoreBuilder
},
{
LanguageConstants.JSONFormatEnumString,
"%s[Compact:\"\", IndentFour:\"4\", IgnoreBinaryData:\"G\", IncludeBinaryData:\"B\", IgnoreUnsupportedTypes:\"I\"]"
"%s[Compact:\"\", IndentFour:\"4\", IgnoreBinaryData:\"G\", IncludeBinaryData:\"B\", IgnoreUnsupportedTypes:\"I\", UnwrapValueTables:\"U\"]"
},
{
LanguageConstants.TraceSeverityEnumString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ internal FormulaValue Process()

using MemoryStream memoryStream = new MemoryStream();
using Utf8JsonWriter writer = new Utf8JsonWriter(memoryStream, new JsonWriterOptions() { Indented = flags.IndentFour, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
Utf8JsonWriterVisitor jsonWriterVisitor = new Utf8JsonWriterVisitor(writer, _timeZoneInfo);
Utf8JsonWriterVisitor jsonWriterVisitor = new Utf8JsonWriterVisitor(writer, _timeZoneInfo, unwrapValueTables: flags.UnwrapValueTables);

_arguments[0].Visit(jsonWriterVisitor);
writer.Flush();
Expand Down Expand Up @@ -89,6 +89,7 @@ private JsonFlags GetFlags()
flags.IgnoreUnsupportedTypes = arg1string.Value.Contains("I");
flags.IncludeBinaryData = arg1string.Value.Contains("B");
flags.IndentFour = arg1string.Value.Contains("4");
flags.UnwrapValueTables = arg1string.Value.Contains("U");
}

if (_arguments.Length > 1 && _arguments[1] is OptionSetValue arg1optionset)
Expand All @@ -97,6 +98,7 @@ private JsonFlags GetFlags()
flags.IgnoreUnsupportedTypes = arg1optionset.Option == "IgnoreUnsupportedTypes";
flags.IncludeBinaryData = arg1optionset.Option == "IncludeBinaryData";
flags.IndentFour = arg1optionset.Option == "IndentFour";
flags.UnwrapValueTables = arg1optionset.Option == "UnwrapValueTables";
}

if ((flags.IncludeBinaryData && flags.IgnoreBinaryData) ||
Expand All @@ -113,13 +115,15 @@ private class Utf8JsonWriterVisitor : IValueVisitor
{
private readonly Utf8JsonWriter _writer;
private readonly TimeZoneInfo _timeZoneInfo;
private readonly bool _unwrapValueTables;

internal readonly List<ErrorValue> ErrorValues = new List<ErrorValue>();

internal Utf8JsonWriterVisitor(Utf8JsonWriter writer, TimeZoneInfo timeZoneInfo)
internal Utf8JsonWriterVisitor(Utf8JsonWriter writer, TimeZoneInfo timeZoneInfo, bool unwrapValueTables)
{
_writer = writer;
_timeZoneInfo = timeZoneInfo;
_unwrapValueTables = unwrapValueTables;
}

public void Visit(BlankValue blankValue)
Expand Down Expand Up @@ -254,6 +258,17 @@ public void Visit(TableValue tableValue)
{
_writer.WriteStartArray();

var isSingleColumnValueTable = false;
if (_unwrapValueTables)
{
var fieldTypes = tableValue.Type.GetFieldTypes();
var firstField = fieldTypes.FirstOrDefault();
if (firstField != null && !fieldTypes.Skip(1).Any() && firstField.Name.Value == "Value")
CarlosFigueiraMSFT marked this conversation as resolved.
Show resolved Hide resolved
{
isSingleColumnValueTable = true;
}
}

foreach (DValue<RecordValue> row in tableValue.Rows)
{
if (row.IsBlank)
Expand All @@ -266,7 +281,15 @@ public void Visit(TableValue tableValue)
}
else
{
row.Value.Visit(this);
if (isSingleColumnValueTable)
{
var namedValue = row.Value.Fields.First();
namedValue.Value.Visit(this);
}
else
{
row.Value.Visit(this);
}
}
}

Expand Down Expand Up @@ -311,6 +334,8 @@ private class JsonFlags
internal bool IncludeBinaryData = false;

internal bool IndentFour = false;

internal bool UnwrapValueTables = false;
}

private static DateTime ConvertToUTC(DateTime dateTime, TimeZoneInfo fromTimeZone)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,25 @@ Error({Kind:ErrorKind.Div0})
// Error records
>> JSON(Filter([-2,-1,0,1,2], 1/Value>0))
Error({Kind:ErrorKind.Div0})

// Unwrapped tables
>> JSON([1, 2, 3], JSONFormat.UnwrapValueTables)
"[1,2,3]"
CarlosFigueiraMSFT marked this conversation as resolved.
Show resolved Hide resolved

>> JSON({a:["one", "two"]}, JSONFormat.UnwrapValueTables)
"{""a"":[""one"",""two""]}"

>> JSON([true, false, true], JSONFormat.UnwrapValueTables)
"[true,false,true]"

// Only unwrap single-column tables where the column name is 'Value'
>> JSON([{a:1}, {a:2}], JSONFormat.UnwrapValueTables)
"[{""a"":1},{""a"":2}]"

// No difference between blank records and blank values
>> JSON([{Value:1},Blank(),{Value:3},{Value:Blank()},{Value:5}], JSONFormat.UnwrapValueTables)
"[1,null,3,null,5]"

// Unwrapping nested tables
>> JSON([[1,2,3],[4,5],[6]], JSONFormat.UnwrapValueTables)
"[[1,2,3],[4,5],[6]]"
23 changes: 23 additions & 0 deletions src/tests/Microsoft.PowerFx.Core.Tests/TexlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,10 @@ public void TexlFunctionTypeSemanticsRenameColumns_ColumnNamesAsIdentifiersDisab
[InlineData("RenameColumns([{A:\"hello\",B:1}], \"A\", \"B\")")]
[InlineData("RenameColumns([{A:\"hello\",B:1}], A, A1, A, A2)")]
[InlineData("RenameColumns(true, A, A1)")]
[InlineData("RenameColumns([{A:\"hello\",B:1}], '', X)")]
[InlineData("RenameColumns([{A:\"hello\",B:1}], ' ' X)")]
[InlineData("RenameColumns([{A:\"hello\",B:1}], '\r\n', X)")]
[InlineData("RenameColumns([{A:\"hello\",B:1}], A, '')")]
public void TexlFunctionTypeSemanticsRenameColumns_Negative(string expression)
{
var engine = new Engine(new PowerFxConfig());
Expand All @@ -363,6 +367,25 @@ public void TexlFunctionTypeSemanticsRenameColumns_Negative(string expression)
Assert.False(result.IsSuccess);
}

[Fact]
public void CheckParseSuccess()
{
var config = new PowerFxConfig();
var engine = new Engine(config);

// var parse = engine.Parse("If(Len(x) = 0, y, x)");
var parse = engine.Parse("If(Len(x) = 0, x, y)");

var r = RecordType.Empty()
.Add(new NamedFormulaType("x", FormulaType.String))
.Add(new NamedFormulaType("y", FormulaType.UntypedObject));

var check = engine.Check(parse, r);
Assert.True(check.IsSuccess);

Assert.IsType<StringType>(check.ReturnType);
}
CarlosFigueiraMSFT marked this conversation as resolved.
Show resolved Hide resolved

[Theory]
[InlineData("IfError(\"Hello\", \"one\")", "s")]
[InlineData("IfError(\"Hello\", 1)", "s")]
Expand Down