feat: emit native types instead of Newtonsoft JObject/JArray in public APIs

Replace all Newtonsoft.Json.Linq types (JObject, JArray, JToken) exposed
in public service methods, model properties, and cmdlet OutputTypes with
native .NET types (Dictionary<string, object?>, List<Dictionary<string,
object?>>, PSObject).

New utilities:
- JsonHelper: ToNative, ToDictionary, ToListOfDictionaries for recursive
  JToken→native conversion
- NativeListConverter / NativeDictionaryConverter: JsonConverters for
  model properties that deserialize JArray/JObject to native types

Services updated (8 methods):
- NodeService: GetNodeConfig, GetNodeDns → Dictionary<string, object?>
- BackupService: GetNotBackedUp → List<Dictionary<string, object?>>
- ClusterConfigService: GetClusterConfig, GetTotem, GetQdevice → Dictionary
- HaService: GetManagerStatus → Dictionary<string, object?>
- VmService: GetGuestExecStatus → Dictionary<string, object?>

Models updated (2 properties):
- PvePool.Members: JArray → List<Dictionary<string, object?>>
- PveHaRule.Properties: JObject → Dictionary<string, object?>

Cmdlets updated (5):
- GetPveClusterConfigCmdlet: OutputType JToken → Dictionary<string, object>
- GetPveNodeConfigCmdlet: iteration updated for Dictionary
- GetPveNodeDnsCmdlet: iteration updated for Dictionary
- GetPveBackupInfoCmdlet: iteration updated for List<Dictionary>
- InvokePveVmGuestExecCmdlet: polling updated for Dictionary

Added D013 to DECISIONS.md: cmdlets must emit only native or module-defined types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-25 11:12:57 -05:00
parent 20ed5de69e
commit 3ce19a721c
17 changed files with 210 additions and 45 deletions
@@ -0,0 +1,60 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
namespace PSProxmoxVE.Core.Utilities
{
/// <summary>
/// Provides helper methods for converting Newtonsoft.Json types to native .NET types.
/// </summary>
public static class JsonHelper
{
/// <summary>
/// Converts a JToken to a native .NET type recursively.
/// JObject becomes Dictionary&lt;string, object&gt;, JArray becomes List&lt;object&gt;, JValue becomes primitive.
/// </summary>
public static object? ToNative(JToken? token)
{
if (token == null || token.Type == JTokenType.Null)
return null;
switch (token)
{
case JObject obj:
return obj.Properties().ToDictionary(
p => p.Name,
p => ToNative(p.Value));
case JArray arr:
return arr.Select(ToNative).ToList();
case JValue val:
return val.Value;
default:
return token.ToString();
}
}
/// <summary>
/// Converts a JObject to a Dictionary&lt;string, object?&gt;.
/// </summary>
public static Dictionary<string, object?> ToDictionary(JObject? obj)
{
if (obj == null) return new Dictionary<string, object?>();
return obj.Properties().ToDictionary(
p => p.Name,
p => ToNative(p.Value));
}
/// <summary>
/// Converts a JArray to a List of Dictionaries.
/// Each element should be a JObject; non-object elements are skipped.
/// </summary>
public static List<Dictionary<string, object?>> ToListOfDictionaries(JArray? arr)
{
if (arr == null) return new List<Dictionary<string, object?>>();
return arr.OfType<JObject>().Select(ToDictionary).ToList();
}
}
}