Skip to content

Updates implementation of allowing null valued properties to handle array and object schema types differently #3141

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

Merged
merged 11 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion autorest.powershell
11 changes: 9 additions & 2 deletions src/readme.graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
azure: false
powershell: true
version: latest
#use: "@autorest/powershell@3.0.509"
use: "$(this-folder)../autorest.powershell"
export-properties-for-dict: false
metadata:
Expand Down Expand Up @@ -651,7 +650,15 @@ directive:
// Fix double = in date parameter. Temp fix for https://github.com/Azure/autorest.powershell/issues/1025.
let dateAssignmentRegex = /(date="\n.*)(\+.*"=")(.*\+.*date)/gmi
$ = $.replace(dateAssignmentRegex, '$1 $3');
return $;

// Allow sending of serialized null properties located in cleanedBody
$ = $.replace(/request\.Content\s*=\s*new\s+global::System\.Net\.Http\.StringContent\(\s*null\s*!=\s*body\s*\?\s*body\.ToJson\(null\)\.ToString\(\)\s*:\s*@"{}",\s*global::System\.Text\.Encoding\.UTF8\);/g,'request.Content = new global::System.Net.Http.StringContent(cleanedBody, global::System.Text.Encoding.UTF8);');

$ = $.replace(/request\.Content\s*=\s*new\s+global::System\.Net\.Http\.StringContent\(\s*null\s*!=\s*body\s*\?\s*new\s+Microsoft\.Graph\.PowerShell\.Runtime\.Json\.XNodeArray\(.*?\)\s*:\s*null,\s*global::System\.Text\.Encoding\.UTF8\);/g,'request.Content = new global::System.Net.Http.StringContent(cleanedBody, global::System.Text.Encoding.UTF8);');

$ = $.replace(/request\.Content\s*=\s*new\s+global::System\.Net\.Http\.StringContent\(\s*null\s*!=\s*body\s*\?\s*new\s+Microsoft\.Graph\.Beta\.PowerShell\.Runtime\.Json\.XNodeArray\(.*?\)\s*:\s*null,\s*global::System\.Text\.Encoding\.UTF8\);/g,'request.Content = new global::System.Net.Http.StringContent(cleanedBody, global::System.Text.Encoding.UTF8);');

return $
}

# Fix enums with underscore.
Expand Down
79 changes: 51 additions & 28 deletions tools/Custom/JsonExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,62 +1,85 @@
namespace Microsoft.Graph.PowerShell.JsonUtilities
namespace NamespacePrefixPlaceholder.PowerShell.JsonUtilities
{
using Newtonsoft.Json.Linq;
using System;
using System.Linq;

public static class JsonExtensions
{
/// <summary>
/// Removes JSON properties that have a value of "defaultnull" and converts properties with values of "null" to actual JSON null values.
/// Recursively removes properties with the value "defaultnull" from a JSON structure
/// and replaces string values that are "null" with actual null values.
/// This method supports both JObject (JSON objects) and JArray (JSON arrays),
/// ensuring proper cleanup of nested structures.
/// </summary>
/// <param name="jsonObject">The JObject to process and clean.</param>
/// <returns>
/// A JSON string representation of the cleaned JObject with "defaultnull" properties removed and "null" values converted to JSON null.
/// </returns>
/// <param name="token">The JToken (JObject or JArray) to process.</param>
/// <returns>The cleaned JSON string with "defaultnull" values removed and "null" strings converted to null.</returns>
/// <example>
/// JObject json = JObject.Parse(@"{""name"": ""John"", ""email"": ""defaultnull"", ""address"": ""null""}");
/// string cleanedJson = json.RemoveDefaultNullProperties();
/// Console.WriteLine(cleanedJson);
/// // Output: { "name": "John", "address": null }
/// </example>
public static string RemoveDefaultNullProperties(this JObject jsonObject)
public static string RemoveDefaultNullProperties(this JToken token)
{
try
{
foreach (var property in jsonObject.Properties().ToList())
if (token is JObject jsonObject)
{
if (property.Value.Type == JTokenType.Object)
foreach (var property in jsonObject.Properties().ToList())
{
RemoveDefaultNullProperties((JObject)property.Value);
}
else if (property.Value.Type == JTokenType.Array)
{
foreach (var item in property.Value)
if (property.Value.Type == JTokenType.Object)
{
if (item.Type == JTokenType.Object)
{
RemoveDefaultNullProperties((JObject)item);
}
RemoveDefaultNullProperties(property.Value);
}
else if (property.Value.Type == JTokenType.Array)
{
RemoveDefaultNullProperties(property.Value);
}
else if (property.Value.Type == JTokenType.String && property.Value.ToString().Equals("defaultnull",StringComparison.Ordinal))
{
property.Remove();
}
else if (property.Value.Type == JTokenType.String && property.Value.ToString().Equals("null",StringComparison.Ordinal))
{
property.Value = JValue.CreateNull();
}
}
else if (property.Value.Type == JTokenType.String && property.Value.ToString() == "defaultnull")
{
property.Remove();
}
else if (property.Value.Type == JTokenType.String && (property.Value.ToString() == "null"))
}
else if (token is JArray jsonArray)
{
// Process each item in the JArray
for (int i = jsonArray.Count - 1; i >= 0; i--)
{
property.Value = JValue.CreateNull();
var item = jsonArray[i];

if (item.Type == JTokenType.Object)
{
RemoveDefaultNullProperties(item);
}
else if (item.Type == JTokenType.String && item.ToString().Equals("defaultnull",StringComparison.Ordinal))
{
jsonArray.RemoveAt(i); // Remove the "defaultnull" string from the array
}
else if (item.Type == JTokenType.String && item.ToString().Equals("null",StringComparison.Ordinal))
{
jsonArray[i] = JValue.CreateNull(); // Convert "null" string to actual null
}
}
}
}
catch (System.Exception)
catch (System.Exception ex)
{
return jsonObject.ToString(); // Return the original string if parsing fails
Console.WriteLine($"Error cleaning JSON: {ex.Message}");
return token.ToString(); // Return the original JSON if any error occurs
}
return jsonObject.ToString();

return token.ToString();
}

public static string ReplaceAndRemoveSlashes(this string body)
{
return body.Replace("/", "").Replace("\\", "").Replace("rn", "").Replace("\"{", "{").Replace("}\"", "}");
}
}
}
}
26 changes: 24 additions & 2 deletions tools/Tests/JsonUtilitiesTest/JsonExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System;
using Newtonsoft.Json.Linq;
using Xunit;
using Microsoft.Graph.PowerShell.JsonUtilities;
using NamespacePrefixPlaceholder.PowerShell.JsonUtilities;

public class JsonExtensionsTests
{
Expand Down Expand Up @@ -45,7 +45,7 @@ public void RemoveDefaultNullProperties_ShouldConvertStringNullToJsonNull()

// Assert
Assert.Null(result["position"]?.Value<string>());
Assert.Equal("",result["team"]?.ToString());
Assert.Equal("", result["team"]?.ToString());
Assert.Equal("Tim", result["displayname"]?.ToString());
Assert.Equal(2000000, result["salary"]?.ToObject<int>());
}
Expand Down Expand Up @@ -124,5 +124,27 @@ public void RemoveDefaultNullProperties_ShouldNotAlterValidData()
Assert.Equal("mayabi@example.com", result["email"]?.ToString());
Assert.Equal(2000000, result["salary"]?.ToObject<int>());
}

//Add tests for json arrays
[Fact]
public void RemoveDefaultNullProperties_ShouldRemoveDefaultNullValuesInJsonArray()
{
// Arrange
JArray json = JArray.Parse(@"[
{ ""displayname"": ""Tim"", ""email"": ""defaultnull"" }

]");

// Act
string cleanedJson = json.RemoveDefaultNullProperties();
JArray result = JArray.Parse(cleanedJson);

// Assert
Assert.Equal("Tim", result[0]?["displayname"]?.ToString());
Assert.False(result[0].ToObject<JObject>().ContainsKey("email"));

}


}

Loading