在C#中将PSObjects(或PS命令)转换为JSON

4

我正在尝试创建一个Web API,将PowerShell命令的输出转换为JSON。是否有可以完成此操作或将PSObject转换为JSON的库?

PSObject属性根据生成它的命令而变化。我尝试将PSObject传递给"ConvertTo-Json",但是得到了额外的对象信息。

PowerShell命令:

Get-Process | Select -Property Handles,ProcessName | ConvertTo-Json

JsonConvert.SerializeObject()之后的输出结果:
"{\"CliXml\":\"<Objs Version=\\\"1.1.0.1\\\" xmlns=\\\"http://schemas.microsoft.com/powershell/2004/04\\\">\\r\\n  <Obj RefId=\\\"0\\\">\\r\\n<TN RefId=\\\"0\\\">\\r\\n <T>System.String</T>\\r\\n      <T>System.Object</T>\\r\\n    </TN>\\r\\n    <ToString>[_x000D__x000A_    {_x000D__x000A_\\\"Handles\\\":163,_x000D__x000A_\\\"ProcessName\\\":\\\"AppleMobileDeviceService\\\"_x000D__x000A_},_x000D__x000A_    {_x000D__x000A_\\\"Handles\\\":  972,_x000D__x000A_\\\"ProcessName\\\":\\\"CcmExec\\\"_x000D__x000A_},_x000D__x000A_{_x000D__x000A_\\\"Handles\\\":  1838,_x000D__x000A_\\\"ProcessName\\\":\\\"ccSvcHst\\\"_x000D__x000A_}"

PowerShell命令用于BeginInvoke。

PowerShell命令:

Get-Process | Select -Property Handles,ProcessName

JsonConvert.SerializeObject(PSObj)的输出结果为:
"[{\"CliXml\":\"<Objs Version=\\\"1.1.0.1\\\" xmlns=\\\"http://schemas.microsoft.com/powershell/2004/04\\\">\\r\\n  <Obj RefId=\\\"0\\\">\\r\\n    <TN RefId=\\\"0\\\">\\r\\n      <T>Selected.System.Diagnostics.Process</T>\\r\\n      <T>System.Management.Automation.PSCustomObject</T>\\r\\n      <T>System.Object</T>\\r\\n    </TN>\\r\\n    <ToString>@{Handles=163; ProcessName=AppleMobileDeviceService}</ToString>\\r\\n    <Obj RefId=\\\"1\\\">\\r\\n      <TNRef RefId=\\\"0\\\" />\\r\\n      <MS>\\r\\n        <I32 N=\\\"Handles\\\">163</I32>\\r\\n        <S N=\\\"ProcessName\\\">AppleMobileDeviceService</S></Objs>\"}}]

1
“ConvertTo-JSON”听起来正是你需要的...那些额外的属性是什么?如果你想要删除属性,你可以使用select-object指定只有那些你需要的属性,然后再转换为json。 - JohnLBevan
那个没有起作用,已更新帖子并附上数据。 - RDs
在我看来,Convert-Json不是一个好的解决方案,因为它将枚举转换为它们的序数值。请参阅此答案https://dev59.com/SVcP5IYBdhLWcg3wYo_i - Rob McFeely
3个回答

2
如果您愿意使用第三方库,那么JSON.NET提供了JsonConvert类。它的使用相当简单:JsonConvert.SerializeObject(myObj)将返回一个包含JSON的字符串。
我认为这比尝试从C#代码中编程调用PowerShell要容易一些。
如果有额外的属性需要省略,那么JsonConvert允许您实现IContractResolver来指定您想要的属性。
最终看起来会像这样:
JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new MyContractResolver() });

ContractResolver 代码如下:

public class MyContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
        properties = properties.Where(p => p.PropertyName != "OmittedProperty").ToList();
        return properties;
    }
}

当然,您可以将属性的条件设置为任何您喜欢的内容。

1
请注意,在 Visual Studio 中检查字符串时,它会添加前导和尾随引号,以及转义引号和反斜杠。因此,双引号和反斜杠将分别显示为 " 和 \。这是否是您在输出中注意到的问题? - Tonkleton
我会检查您发布的代码。感谢您的帮助。这不仅限于VS。我将数据输出到控制台窗口,然后还将数据输出到Web浏览器,并看到了相同的问题。 - RDs
你有没有设法解决这个问题?我也遇到了同样的问题。问题出在PSObject的序列化器上,它总是将PSObject的属性序列化为CliXML。我尝试枚举属性(同时将它们添加到集合中)取得了一些成功,但是一些cmdlets(比如get-childitem和get-command)在尝试读取对象的属性时会引发一个runspaceexception异常。 - bluuf
请注意,“ConvertTo-Json”不是一个独立的模块。它是 Powershell 的一部分。将其拆分为单独的库是一个开放问题。请参见:https://github.com/PowerShell/PowerShell/issues/3995 - Friendly Genius

2

如果还有人在寻找解决方案,可以利用PowerShell中内置的JSON转换器(ConvertTo-Json)来实现:

using System;
using System.Management.Automation;
using Microsoft.PowerShell.Commands;
using System.Collections.ObjectModel;

namespace PowerJson
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create the Powershell instance, and adds command to be executed
            PowerShell ps = PowerShell.Create();
            ps.AddCommand("Get-Process");
            Collection<PSObject> _ps_results = ps.Invoke();

            // Create a JSON Context Object
            JsonObject.ConvertToJsonContext _json_context = new JsonObject.ConvertToJsonContext(maxDepth: 12, enumsAsStrings: false, compressOutput: false);

            // Converts the PSObject into JSON
            string json_result = JsonObject.ConvertToJson(_ps_results, _json_context);

            // Outputs the result to console
            Console.WriteLine(json_result);
        }
    }
}

0

这对我有效。试试看吧。

if (item.TypeNames.FirstOrDefault() == "System.String")
{
    return item.BaseObject.ToString();
}
else
{
    var settings = new Newtonsoft.Json.JsonSerializerSettings
        { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };

    var dict = new Dictionary<string, object>();
    var objMembers = typeof(object).GetMembers();
    var ignoreMembers = new List<string>();

    ignoreMembers.AddRange(
        item.Members.Where(
            i => i.TypeNameOfValue.StartsWith("Deserialized.")).Select(i => i.Name).ToList());

    ignoreMembers.AddRange(objMembers.Select(i => i.Name));

    var filteredMembers =
        item.Members.Where(
            i => ignoreMembers.All(
                ig => ig.ToLower() != i.Name.ToLower())).ToList();

    foreach (var mem in filteredMembers)
    {
        if (!dict.ContainsKey(mem.Name))
        {
            dict.Add(mem.Name, "");
        }
        dict[mem.Name] = mem.Value;
    }

    try
    {
        return Newtonsoft.Json.JsonConvert.SerializeObject(dict, settings);
    }
    catch (Exception e)
    {
    }
}
return null;

这是一段代码片段。它应该在什么情境下运行? - Peter Mortensen
抱歉,错过了这个评论。它获取项,该项是从 powershell 进程返回的项。然后迭代这些内容以将响应序列化为 json。我已经加强了这段代码,但它仍然能够完成它的目的。 - Jimmie Clark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接