class Program { static void Main(string[] args) { List companies = new List { new Company { Name = "Initrode Global", Aliases = new List { "Initech" }, Employees = new List { new Employee(22, "Bill Lumbergh", new DateTime(2005, 3, 25)), new Employee(87, "Peter Gibbons", new DateTime(2011, 6, 3)), new Employee(91, "Michael Bolton", new DateTime(2012, 10, 18)), } }, new Company { Name = "Contoso Corporation", Aliases = new List { "Contoso Bank", "Contoso Pharmaceuticals" }, Employees = new List { new Employee(23, "John Doe", new DateTime(2007, 8, 22)), new Employee(61, "Joe Schmoe", new DateTime(2009, 9, 12)), } } }; JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Converters.Add(new ListCompactionConverter()); settings.Converters.Add(new IsoDateTimeConverter { DateTimeFormat = "dd-MMM-yyyy" }); settings.Formatting = Formatting.Indented; string json = JsonConvert.SerializeObject(companies, settings); Console.WriteLine(json); Console.WriteLine(); List list = JsonConvert.DeserializeObject>(json, settings); foreach (Company c in list) { Console.WriteLine("Company: " + c.Name); Console.WriteLine("Aliases: " + string.Join(", ", c.Aliases)); Console.WriteLine("Employees: "); foreach (Employee emp in c.Employees) { Console.WriteLine(" Id: " + emp.Id); Console.WriteLine(" Name: " + emp.Name); Console.WriteLine(" HireDate: " + emp.HireDate.ToShortDateString()); Console.WriteLine(); } Console.WriteLine(); } } } class Company { public string Name { get; set; } [JsonProperty("Doing Business As")] public List Aliases { get; set; } public List Employees { get; set; } } class Employee { [JsonConstructor] public Employee(int id, string name, DateTime hireDate) { Id = id; Name = name; HireDate = hireDate; } public int Id { get; private set; } public string Name { get; private set; } public DateTime HireDate { get; private set; } }
public class CustomJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) { return true; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (!(objectType.IsGenericType)) return null; var deserializedList = (IList)Activator.CreateInstance(objectType); var jArray = JArray.Load(reader); var underlyingType = objectType.GetGenericArguments().Single(); var properties = underlyingType.GetProperties(); Type[] types = new Type[properties.Length]; for (var i = 0; i < properties.Length; i++) { types[i] = properties[i].PropertyType; } var values = jArray.Skip(1); foreach (JArray value in values) { var propertiesValues = new object[properties.Length]; for (var i = 0; i < properties.Length; i++) { propertiesValues[i] = Convert.ChangeType(value[i], properties[i].PropertyType); } var constructor = underlyingType.GetConstructor(types); var obj = constructor.Invoke(propertiesValues); deserializedList.Add(obj); } return deserializedList; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (!(value.GetType().IsGenericType) || !(value is IList)) return; var val = value as IList; PropertyInfo[] properties = val.GetType().GetGenericArguments().Single().GetProperties(); writer.WriteStartArray(); writer.WriteStartArray(); foreach (var p in properties) writer.WriteValue(p.Name); writer.WriteEndArray(); foreach (var v in val) { writer.WriteStartArray(); foreach (var p in properties) writer.WriteValue(v.GetType().GetProperty(p.Name).GetValue(v)); writer.WriteEndArray(); } writer.WriteEndArray(); } }
并使用以下行进行序列化:
var jsonStr = JsonConvert.SerializeObject(list, new CustomJsonConverter());
要将字符串反序列化为typeof(MyTestClass)中的对象列表,请使用以下行:
var reconstructedList = JsonConvert.DeserializeObject>(jsonStr, new CustomJsonConverter());
[ // Root is an array. ["Key1","Key2"], // Array literal. ["$[*]","$.Key1"], // Take all of the elements in the original array '$[*]' // and use the value under the "Key1" property '$.Key1' ["$[*]","$.Key2"] // Similiarly for the "Key2" property ]
注意有一个边缘情况,您希望将值映射到具有两个元素的文字数组。 这将无法正常工作。
映射后,您可以反序列化(Manatee.Json也能为您做到这一点)。
编辑
我意识到我没有在我的答案中加入任何代码,所以在这里。
JsonValue source = new JsonArray { new JsonObject {{"Key1", 87}, {"Key2", 99}}, new JsonObject {{"Key1", 42}, {"Key2", -8}} }; JsonValue template = new JsonArray { new JsonArray {"Key1", "Key2"}, new JsonArray {"$[*]", "$.Key1"}, new JsonArray {"$[*]", "$.Key2"} }; var result = source.Transform(template);
而已。
编辑2
我在设计反向翻译时遇到了麻烦,所以这就是你如何仅通过序列化来实现这一点。
您需要注册几个方法来自己执行映射和序列化。 实质上,您指示序列化程序如何构建和解构JSON。
您的数据模型:
public class MyData { public int Key1 { get; set; } public int Key2 { get; set; } }
序列化方法:
public static class MyDataListSerializer { public static JsonValue ToJson(List data, JsonSerializer serializer) { return new JsonArray { new JsonArray {"Key1", "Key2"}, new JsonArray(data.Select(d => d.Key1)), new JsonArray(data.Select(d => d.Key2)), }; } public static MyData FromJson(JsonValue value, JsonSerializer serializer) { return value.Array.Skip(1) .Array.Select((jv, i) => new MyData { Key1 = (int) jv.Number, Key2 = value.Array[2].Array[i] }; } }
public string Serialize(MyData data) { // _serializer is an instance field of type JsonSerializer return _serializer.Serialize(data).ToString(); } public MyData Deserialize(Stream stream) { var json = JsonValue.Parse(stream); return _serializer.Deserialize(json); }
[ "$[1][*]", // Get all of the items in the first value list { "Key1":"@", // Key1 is sourced from the item returned by '$[1][*]' "Key2":"$[2][*]" // Key2 is sourced from the items in the second element // of the original source (not the item returned by '$[1][*]') } ]
现在,您可以转换两个方向,而不必使用自定义序列化方法执行任何操作。
串行器现在看起来像这样:
public string Serialize(MyData data) { // _serializer is an instance field of type JsonSerializer var json = _serializer.Serialize(data); // _transformTemplate is an instance field of type JsonValue // representing the first template from above. var transformedJson = json.Transform(_transformTemplate); return transformedJson.ToString(); } public MyData Deserialize(Stream stream) { var json = JsonValue.Parse(stream); // _reverseTransformTemplate is an instance field of type JsonValue // representing the second template from above. var untransformedJson = json.Transform(_reverseTransformTemplate); return _serializer.Deserialize(untransformedJson); }
// will be serialized as: {"Key1":87,"Key2":99} public class Foo { public string Key1; public string Key2; }
…写这个:
// will be serialized as: [87,99] public class Foo : IEnumerable
如果您需要将此策略应用于许多类,那么您可以声明一个抽象基类来摆脱一些样板:
// Base class for objects to be serialized as "[...]" instead of "{...}" public abstract class SerializedAsArray : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => EnumerateFields().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => EnumerateFields().GetEnumerator(); protected abstract IEnumerable EnumerateFields(); } // will be serialized as: [87,99] public class Foo : SerializedAsArray { public string Key1; public string Key2; protected override IEnumerable EnumerateFields() { yield return Key1; yield return Key2; } }