我有以下课程,但我没有成功尝试序列化为Json。
class HL7 : NameValueCollection
{
public List<HL7> Children { get; set; }
public HL7()
{
Children = new List<HL7>();
}
}
我已经创建了这样的对象并向其添加了数据:
HL7 hl7 = new HL7();
hl7.Add("a", "123");
hl7.Add("b", "456");
hl7.Children.Add(new HL7());
hl7.Children[0].Add("c", "123");
hl7.Children[0].Add("d", "456");
当我打电话
JsonConvert.SerializeObject(hl7)
我收到了
["a","b"]
我期待以下内容:
{
"a": "123",
"b": "456",
"Children": [
{
"c": "123",
"d": "456",
}
]
}
答案 0 :(得分:5)
这里有一些事情发生:
Json.NET无法在没有自定义转换器的情况下序列化NameValueCollection
因为NameValueCollection
实现了IEnumerable
来迭代密钥,但是没有实现IDictionary
进行迭代超过键和值。有关为何导致Json.NET出现问题的详细解释,请参阅this answer。
因为NameValueCollection
实现了IEnumerable
,所以Json.NET将您的类视为集合,因此将其序列化为JSON数组而不是具有命名属性的JSON对象。因此,您的Children
未序列化。同样,需要一个自定义转换器来解决这个问题。
假设上述问题已得到解决,如果HL7
的{{1}}子类恰好有一个名为NameValueCollection
的密钥,则在序列化时会生成无效的JSON,即一个对象具有重复的属性名称。我建议移动名字和为了明确的序列化,将值放入嵌套属性(命名为例如&#34;值&#34;)。
"Children"
实际上可以为给定的键字符串提供多个字符串值,因此其条目值需要序列化为JSON数组而不是单个字符串。
将所有这些放在一起,以下代码:
NameValueCollection
使用以下测试用例:
[JsonConverter(typeof(HL7Converter))]
public class HL7 : NameValueCollection
{
public List<HL7> Children { get; set; }
public HL7()
{
Children = new List<HL7>();
}
}
public class HL7Converter : JsonConverter
{
class HL7Proxy
{
public NameValueCollectionDictionaryWrapper Values { get; set; }
public List<HL7> Children { get; set; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(HL7);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var proxy = serializer.Deserialize<HL7Proxy>(reader);
if (proxy == null)
return existingValue;
var hl7 = existingValue as HL7;
if (hl7 == null)
hl7 = new HL7();
hl7.Add(proxy.Values.GetCollection());
if (proxy.Children != null)
hl7.Children.AddRange(proxy.Children);
return hl7;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
HL7 hl7 = (HL7)value;
if (hl7 == null)
return;
serializer.Serialize(writer, new HL7Proxy { Children = hl7.Children, Values = new NameValueCollectionDictionaryWrapper(hl7) });
}
}
// Proxy dictionary to serialize & deserialize a NameValueCollection. We use a proxy dictionary rather than a real dictionary because NameValueCollection is an ordered collection but the generic dictionary class is unordered.
public class NameValueCollectionDictionaryWrapper: IDictionary<string, string []>
{
readonly NameValueCollection collection;
public NameValueCollectionDictionaryWrapper()
: this(new NameValueCollection())
{
}
public NameValueCollectionDictionaryWrapper(NameValueCollection collection)
{
this.collection = collection;
}
// Method instead of a property to guarantee that nobody tries to serialize it.
public NameValueCollection GetCollection()
{
return collection;
}
#region IDictionary<string,string[]> Members
public void Add(string key, string[] value)
{
if (collection.GetValues(key) != null)
throw new ArgumentException("Duplicate key " + key);
foreach (var str in value)
collection.Add(key, str);
}
public bool ContainsKey(string key)
{
return collection.GetValues(key) != null;
}
public ICollection<string> Keys
{
get {
return collection.AllKeys;
}
}
public bool Remove(string key)
{
bool found = ContainsKey(key);
if (found)
collection.Remove(key);
return found;
}
public bool TryGetValue(string key, out string[] value)
{
value = collection.GetValues(key);
return value != null;
}
public ICollection<string[]> Values
{
get {
return Enumerable.Range(0, collection.Count).Select(i => collection.GetValues(i)).ToArray();
}
}
public string[] this[string key]
{
get
{
var value = collection.GetValues(key);
if (value == null)
throw new KeyNotFoundException();
return value;
}
set
{
Remove(key);
Add(key, value);
}
}
#endregion
#region ICollection<KeyValuePair<string,string[]>> Members
public void Add(KeyValuePair<string, string[]> item)
{
Add(item.Key, item.Value);
}
public void Clear()
{
collection.Clear();
}
public bool Contains(KeyValuePair<string, string[]> item)
{
string [] value;
if (!TryGetValue(item.Key, out value))
return false;
return EqualityComparer<string[]>.Default.Equals(item.Value, value); // Consistent with Dictionary<TKey, TValue>
}
public void CopyTo(KeyValuePair<string, string[]>[] array, int arrayIndex)
{
foreach (var item in this)
array[arrayIndex++] = item;
}
public int Count
{
get { return collection.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(KeyValuePair<string, string[]> item)
{
if (Contains(item))
return Remove(item.Key);
return false;
}
#endregion
#region IEnumerable<KeyValuePair<string,string[]>> Members
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
{
foreach (string key in collection)
{
yield return new KeyValuePair<string, string[]>(key, collection.GetValues(key));
}
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
提供以下JSON:
HL7 hl7 = new HL7();
hl7.Add("a", "123");
hl7.Add("b", "456");
hl7.Add("Children", "Children");
hl7.Children.Add(new HL7());
hl7.Children[0].Add("c", "123");
hl7.Children[0].Add("d", "456");
hl7.Children[0].Add("d", "789");
var json = JsonConvert.SerializeObject(hl7, Formatting.Indented);
Debug.WriteLine(json);
答案 1 :(得分:3)
受到这个答案的启发 how to convert NameValueCollection to JSON string? ,这是工作代码(唯一不好的部分可能是作为属性名称的“Children”字符串。如果你要做一个重构,这将导致错误。
JsonConvert.SerializeObject(NvcToDictionary(hl7, false));
功能:
static Dictionary<string, object> NvcToDictionary(HL7 nvc, bool handleMultipleValuesPerKey)
{
var result = new Dictionary<string, object>();
foreach (string key in nvc.Keys)
{
if (handleMultipleValuesPerKey)
{
string[] values = nvc.GetValues(key);
if (values.Length == 1)
{
result.Add(key, values[0]);
}
else
{
result.Add(key, values);
}
}
else
{
result.Add(key, nvc[key]);
}
}
if (nvc.Children.Any())
{
var listOfChildrenDictionary = new List<Dictionary<string, object>>();
foreach (var nvcChildren in nvc.Children){
listOfChildrenDictionary.Add(NvcToDictionary(nvcChildren, false));
}
result.Add("Children", listOfChildrenDictionary);
}
return result;
}
答案 2 :(得分:1)
我遇到了使用JSON.Net序列化NameValueCollections
的问题,我找到的唯一方法是将其转换为字典,然后将其序列化为:
var jsonString = JsonConvert.SerializeObject(new
{
Parent = hl7.AllKeys.ToDictionary(r => r, r => hl7[r]),
Children = hl7.Children.Select(c => c.AllKeys.ToDictionary(sub => sub, sub => c[sub]))
}, Newtonsoft.Json.Formatting.Indented);
你将最终得到:
{
"Parent": {
"a": "123",
"b": "456"
},
"Children": [
{
"c": "123",
"d": "456"
}
]
}
但是这将返回"Parent"
以及顶级项目,因为您必须为匿名类型的属性指定名称
答案 3 :(得分:1)
这是一个自定义序列化程序,可以按照您的要求编写JSON,附带示例程序。序列化器位于底部。请注意,您需要将此转换器添加到JSON序列化程序设置中,或者通过默认情况下完成,或者通过序列化程序的构造函数。或者,由于您有一个子类,您可以使用HL7类上的JsonConverterAttribute
来分配序列化程序
public class Program
{
static int Main(string[] args) {
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
Converters = new []{ new HL7Converter() }
};
HL7 hl7 = new HL7();
hl7.Add("a", "123");
hl7.Add("b", "456");
hl7.Children.Add(new HL7());
hl7.Children[0].Add("c", "123");
hl7.Children[0].Add("d", "456");
Console.WriteLine (JsonConvert.SerializeObject (hl7));
return 0;
}
}
public class HL7 : NameValueCollection
{
public List<HL7> Children { get; set; }
public HL7()
{
Children = new List<HL7> ();
}
}
public class HL7Converter : Newtonsoft.Json.JsonConverter {
#region implemented abstract members of JsonConverter
public override void WriteJson (Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
var collection = (HL7)value;
writer.WriteStartObject ();
foreach (var key in collection.AllKeys) {
writer.WritePropertyName (key);
writer.WriteValue (collection [key]);
}
writer.WritePropertyName ("Children");
serializer.Serialize (writer,collection.Children);
writer.WriteEndObject ();
}
public override object ReadJson (Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
HL7 collection = existingValue as HL7 ?? new HL7 ();
JObject jObj = JObject.Load (reader);
foreach (var prop in jObj.Properties()) {
if (prop.Name != "Children") {
collection.Add (prop.Name, prop.Value.ToObject<string> ());
} else {
collection.Children = jObj.ToObject<List<HL7>> ();
}
}
return collection;
}
public override bool CanConvert (Type objectType)
{
return objectType == typeof(HL7);
}
#endregion
}