如何将serialize/deserializejson字典转化为数组
How to serialize/deserialize json dictionary into array
需要将 C# 字典序列化和反序列化为 JSON 数组。我还想使用数组索引符号从 powershell 中读取 JSON。
默认情况下,JSON格式为:
{
"defaultSettings": {
"applications": {
"Apollo": {
"environments": {
"DEV": {
"dbKeyTypes": {
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06",
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
},
"TST": {
"dbKeyTypes": {
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06",
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
}
}
},
"Gemini": {
"environments": {
"DEV": {
"dbKeyTypes": {
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06",
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
},
"TST": {
"dbKeyTypes": {
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06",
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
}
}
}
}
}
}
这在 .Net Core 中使用默认的 json reader 效果很好,但它不允许我在 PowerShell 中使用数组索引符号。
相反,我正在寻找的是:
{
"defaultSettings": {
"applications": [
{
"Apollo": {
"environments": [
{
"DEV": {
"dbKeyTypes": [
{
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06"
},
{
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
]
}
},
{
"TST": {
"dbKeyTypes": [
{
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06"
},
{
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
]
}
}
]
}
},
{
"Gemini": {
"environments": [
{
"DEV": {
"dbKeyTypes": [
{
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06"
},
{
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
]
}
},
{
"TST": {
"dbKeyTypes": [
{
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06"
},
{
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
]
}
}
]
}
}
]
}
}
我正在使用
中的 WriteJson 部分
这很好用;但是,当然,由于未实现 ReadJson()
方法,因此不会读取。顺便说一句,为了获得上述所需的 json 格式,我将 link 中的 CustomDictionaryConverter 修改为:
writer.WritePropertyName(key.ToString());
//writer.WriteValue(key);
//writer.WritePropertyName("value");
serializer.Serialize(writer, valueEnumerator.Current);
实现背后的类是:
public enum DeploymentEnvironment { DEV = 1, TST = 2 }
public enum TargetApplication { Apollo = 1, Gemini = 2 }
public enum DbKeyType { DmkPassword = 1, SymmetricKeySource = 2 }
public class DeploymentSettings
{
[JsonProperty("defaultSettings")]
public DefaultSettings DefaultSettings { get; set; }
public DeploymentSettings()
{
DefaultSettings = new DefaultSettings();
}
}
public partial class DefaultSettings
{
[JsonProperty("applications")]
public Dictionary<TargetApplication, ApplicationContainer> Applications { get; set; }
public DefaultSettings()
{
Applications = new Dictionary<TargetApplication, ApplicationContainer>();
}
}
public partial class ApplicationContainer
{
[JsonProperty("environments")]
public Dictionary<DeploymentEnvironment, EnvironmentContainer> Environments { get; set; }
public ApplicationContainer()
{
Environments = new Dictionary<DeploymentEnvironment, EnvironmentContainer>();
}
}
public partial class EnvironmentContainer
{
[JsonProperty("dbKeyTypes")]
public Dictionary<DbKeyType, string> DbKeyTypes { get; set; }
public EnvironmentContainer()
{
DbKeyTypes = new Dictionary<DbKeyType, string>();
}
}
我按如下方式序列化对象:
var json = JsonConvert.SerializeObject(ds, Formatting.Indented, new CustomDictionaryConverter());
如前所述,序列化有效,但我需要帮助编写 ReadJson()
方法才能反序列化。
您可以扩展 CustomDictionaryConverter
来读写,如下所示:
public class CustomDictionaryConverter : JsonConverter
{
// Adapted from CustomDictionaryConverter from this answer
// To
// By Brian Rogers https://whosebug.com/users/10263/brian-rogers
sealed class InconvertibleDictionary : Dictionary<object, object>
{
public InconvertibleDictionary(DictionaryEntry entry)
: base(1)
{
this[entry.Key] = entry.Value;
}
}
public override bool CanConvert(Type objectType)
{
return typeof(IDictionary).IsAssignableFrom(objectType) && objectType != typeof(InconvertibleDictionary);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Lazy evaluation of the enumerable prevents materialization of the entire collection of dictionaries at once.
serializer.Serialize(writer, Entries(((IDictionary)value)).Select(p => new InconvertibleDictionary(p)));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
return null;
var dictionary = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
switch (reader.TokenType)
{
case JsonToken.StartObject:
serializer.Populate(reader, dictionary);
return dictionary;
case JsonToken.StartArray:
{
while (true)
{
switch (reader.ReadToContentAndAssert().TokenType)
{
case JsonToken.EndArray:
return dictionary;
case JsonToken.StartObject:
serializer.Populate(reader, dictionary);
break;
default:
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
}
}
}
default:
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
}
}
static IEnumerable<DictionaryEntry> Entries(IDictionary dict)
{
foreach (DictionaryEntry entry in dict)
yield return entry;
}
}
public static partial class JsonExtensions
{
public static JsonReader ReadToContentAndAssert(this JsonReader reader)
{
return reader.ReadAndAssert().MoveToContentAndAssert();
}
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
然后您可以使用以下设置序列化和反序列化您的 DeploymentSettings
:
var settings = new JsonSerializerSettings
{
Converters = { new CustomDictionaryConverter(), new StringEnumConverter() }
};
var ds = JsonConvert.DeserializeObject<DeploymentSettings>(json, settings);
var json2 = JsonConvert.SerializeObject(ds, Formatting.Indented, settings);
备注:
此版本的转换器避免将整个字典加载到 ReadJson()
或 WriteJson()
中的临时 JArray
层次结构中,而是直接从和到流JSON 流。
因为序列化程序现在用于直接序列化各个字典条目,所以需要 StringEnumConverter
才能正确命名键。 (使用序列化程序还可以确保数字或 DateTime
键正确国际化,如果您在任何地方使用这样的字典。)
因为Json.NET支持注释,转换器检查并跳过它们,这增加了一些复杂性。 (我希望有一种方法可以让 JsonReader
默默地跳过评论。)
演示 fiddle here.
需要将 C# 字典序列化和反序列化为 JSON 数组。我还想使用数组索引符号从 powershell 中读取 JSON。
默认情况下,JSON格式为:
{
"defaultSettings": {
"applications": {
"Apollo": {
"environments": {
"DEV": {
"dbKeyTypes": {
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06",
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
},
"TST": {
"dbKeyTypes": {
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06",
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
}
}
},
"Gemini": {
"environments": {
"DEV": {
"dbKeyTypes": {
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06",
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
},
"TST": {
"dbKeyTypes": {
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06",
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
}
}
}
}
}
}
这在 .Net Core 中使用默认的 json reader 效果很好,但它不允许我在 PowerShell 中使用数组索引符号。
相反,我正在寻找的是:
{
"defaultSettings": {
"applications": [
{
"Apollo": {
"environments": [
{
"DEV": {
"dbKeyTypes": [
{
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06"
},
{
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
]
}
},
{
"TST": {
"dbKeyTypes": [
{
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06"
},
{
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
]
}
}
]
}
},
{
"Gemini": {
"environments": [
{
"DEV": {
"dbKeyTypes": [
{
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06"
},
{
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
]
}
},
{
"TST": {
"dbKeyTypes": [
{
"DmkPassword": "AEikOooIuGxXC9UBJQ3ckDj7Q126tB06"
},
{
"SymmetricKeySource": "bTU7XOAYA2FFifmiBUggu99yHxX3Ftds"
}
]
}
}
]
}
}
]
}
}
我正在使用
这很好用;但是,当然,由于未实现 ReadJson()
方法,因此不会读取。顺便说一句,为了获得上述所需的 json 格式,我将 link 中的 CustomDictionaryConverter 修改为:
writer.WritePropertyName(key.ToString());
//writer.WriteValue(key);
//writer.WritePropertyName("value");
serializer.Serialize(writer, valueEnumerator.Current);
实现背后的类是:
public enum DeploymentEnvironment { DEV = 1, TST = 2 }
public enum TargetApplication { Apollo = 1, Gemini = 2 }
public enum DbKeyType { DmkPassword = 1, SymmetricKeySource = 2 }
public class DeploymentSettings
{
[JsonProperty("defaultSettings")]
public DefaultSettings DefaultSettings { get; set; }
public DeploymentSettings()
{
DefaultSettings = new DefaultSettings();
}
}
public partial class DefaultSettings
{
[JsonProperty("applications")]
public Dictionary<TargetApplication, ApplicationContainer> Applications { get; set; }
public DefaultSettings()
{
Applications = new Dictionary<TargetApplication, ApplicationContainer>();
}
}
public partial class ApplicationContainer
{
[JsonProperty("environments")]
public Dictionary<DeploymentEnvironment, EnvironmentContainer> Environments { get; set; }
public ApplicationContainer()
{
Environments = new Dictionary<DeploymentEnvironment, EnvironmentContainer>();
}
}
public partial class EnvironmentContainer
{
[JsonProperty("dbKeyTypes")]
public Dictionary<DbKeyType, string> DbKeyTypes { get; set; }
public EnvironmentContainer()
{
DbKeyTypes = new Dictionary<DbKeyType, string>();
}
}
我按如下方式序列化对象:
var json = JsonConvert.SerializeObject(ds, Formatting.Indented, new CustomDictionaryConverter());
如前所述,序列化有效,但我需要帮助编写 ReadJson()
方法才能反序列化。
您可以扩展 CustomDictionaryConverter
来读写,如下所示:
public class CustomDictionaryConverter : JsonConverter
{
// Adapted from CustomDictionaryConverter from this answer
// To
// By Brian Rogers https://whosebug.com/users/10263/brian-rogers
sealed class InconvertibleDictionary : Dictionary<object, object>
{
public InconvertibleDictionary(DictionaryEntry entry)
: base(1)
{
this[entry.Key] = entry.Value;
}
}
public override bool CanConvert(Type objectType)
{
return typeof(IDictionary).IsAssignableFrom(objectType) && objectType != typeof(InconvertibleDictionary);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Lazy evaluation of the enumerable prevents materialization of the entire collection of dictionaries at once.
serializer.Serialize(writer, Entries(((IDictionary)value)).Select(p => new InconvertibleDictionary(p)));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
return null;
var dictionary = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
switch (reader.TokenType)
{
case JsonToken.StartObject:
serializer.Populate(reader, dictionary);
return dictionary;
case JsonToken.StartArray:
{
while (true)
{
switch (reader.ReadToContentAndAssert().TokenType)
{
case JsonToken.EndArray:
return dictionary;
case JsonToken.StartObject:
serializer.Populate(reader, dictionary);
break;
default:
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
}
}
}
default:
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
}
}
static IEnumerable<DictionaryEntry> Entries(IDictionary dict)
{
foreach (DictionaryEntry entry in dict)
yield return entry;
}
}
public static partial class JsonExtensions
{
public static JsonReader ReadToContentAndAssert(this JsonReader reader)
{
return reader.ReadAndAssert().MoveToContentAndAssert();
}
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
然后您可以使用以下设置序列化和反序列化您的 DeploymentSettings
:
var settings = new JsonSerializerSettings
{
Converters = { new CustomDictionaryConverter(), new StringEnumConverter() }
};
var ds = JsonConvert.DeserializeObject<DeploymentSettings>(json, settings);
var json2 = JsonConvert.SerializeObject(ds, Formatting.Indented, settings);
备注:
此版本的转换器避免将整个字典加载到
ReadJson()
或WriteJson()
中的临时JArray
层次结构中,而是直接从和到流JSON 流。因为序列化程序现在用于直接序列化各个字典条目,所以需要
StringEnumConverter
才能正确命名键。 (使用序列化程序还可以确保数字或DateTime
键正确国际化,如果您在任何地方使用这样的字典。)因为Json.NET支持注释,转换器检查并跳过它们,这增加了一些复杂性。 (我希望有一种方法可以让
JsonReader
默默地跳过评论。)
演示 fiddle here.