我正在编写Cmdlet,需要将对象结构传递到可能包含PSObject
的API客户端中。当前,这些序列化为包含CLIXML的JSON字符串。相反,我需要像对待对象一样对待它(将PSObject.Properties
中的NoteProperty视为属性,并递归序列化其值)。
我尝试编写自己的JsonConverter
,但由于某种原因,它只被顶层对象调用,而不是嵌套的PSObject
s:
public class PSObjectJsonConverter : JsonConverter {
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
if (value is PSObject) {
JObject obj = new JObject();
foreach (var prop in ((PSObject)value).Properties) {
obj.Add(new JProperty(prop.Name, value));
}
obj.WriteTo(writer);
} else {
JToken token = JToken.FromObject(value);
token.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
throw new NotImplementedException();
}
public override bool CanRead {
get { return false; }
}
public override bool CanConvert(Type objectType) {
return true;
}
}
此外,我正在使用CamelCasePropertyNamesContractResolver
序列化为驼峰式保护套。有没有办法让转换器尊重这一点?
答案 0 :(得分:4)
以下转换器应正确序列化PSObject
类型的递归嵌套对象:
public class PSObjectJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(PSObject).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var psObj = (PSObject)value;
writer.WriteStartObject();
foreach (var prop in psObj.Properties)
{
//Probably we shouldn't try to serialize a property that can't be read.
//https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.pspropertyinfo.isgettable?view=powershellsdk-1.1.0#System_Management_Automation_PSPropertyInfo_IsGettable
if (!prop.IsGettable)
continue;
writer.WritePropertyName(prop.Name);
serializer.Serialize(writer, prop.Value);
}
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanRead { get { return false; } }
}
注意:
在WriteJson
中,将传入对象value
序列化为每个属性的值。当然,您的意思是prop.Value
。
当传入对象类型为true
时,仅从CanConvert()
返回PSObject
,就可以避免为非PSObject
实现默认序列化输入WriteJson()
。
当您致电JToken.FromObject(value)
时,您并未使用传入的JsonSerializer serializer
。因此,任何JsonSerializerSettings
(包括转换器)都将丢失。从理论上讲,您可以改用JToken.FromObject(Object, JsonSerializer)
来保留设置,但是如果这样做,您会遇到 JSON.Net throws StackOverflowException when using [JsonConvert()] 中描述的错误。幸运的是,由于现在需要默认序列化时,我们从false
返回了CanConvert
,因此不再需要。
无需构造中间JObject
。您可以直接写入JsonWriter
,这会提高性能。
更新:此外,我正在使用CamelCasePropertyNamesContractResolver
序列化为驼峰式保护套。有没有办法让转换器尊重这一点?
一旦为类型引入了custom JsonConverter
,就需要手动进行所有操作,包括重新映射属性名称。这是WriteJson()
的一个版本,可以通过使用DefaultContractResolver.NamingStrategy
来处理:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var psObj = (PSObject)value;
writer.WriteStartObject();
var resolver = serializer.ContractResolver as DefaultContractResolver;
var strategy = (resolver == null ? null : resolver.NamingStrategy) ?? new DefaultNamingStrategy();
foreach (var prop in psObj.Properties)
{
//Probably we shouldn't try to serialize a property that can't be read.
//https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.pspropertyinfo.isgettable?view=powershellsdk-1.1.0#System_Management_Automation_PSPropertyInfo_IsGettable
if (!prop.IsGettable)
continue;
writer.WritePropertyName(strategy.GetPropertyName(prop.Name, false));
serializer.Serialize(writer, prop.Value);
}
writer.WriteEndObject();
}
请注意,Json.NET 9.0.1中引入了命名策略,因此,如果您使用的是较早的版本,则需要创建自己的骆驼案例名称映射器,例如this answer中所示的映射器。