反序列化已知和未知字段的json
给定下面的json结果:默认的json结果有一组已知的字段:
{ "id": "7908", "name": "product name" }
但是可以扩展其他字段(在本例中为_unknown_field_name_1
和_unknown_field_name_2
),其中名称在请求结果时是未知的。
{ "id": "7908", "name": "product name", "_unknown_field_name_1": "some value", "_unknown_field_name_2": "some value" }
我希望json结果被序列化和反序列化到已知字段的属性的类中,并将未知字段(没有属性)映射到字典等属性(或多个属性),以便它们可以访问和修改。
public class Product { public string id { get; set; } public string name { get; set; } public Dictionary<string, string> fields { get; set; } }
我想我需要一种方法来插入一个JSON序列化器,并为我自己的遗漏成员做映射(包括序列化和反序列化)。 我一直在寻找各种可能性:
- json.net和自定义合约parsing器(不知道如何去做)
- datacontract序列化器(只能重写序列化,序列化)
- 序列化dynamic和做自定义映射(这可能工作,但似乎很多工作)
- 让产品从DynamicObjectinheritance(序列化器使用reflection,不要调用trygetmember和trysetmember方法)
我正在使用restsharp,但任何序列化程序可以插入。
哦,我不能改变JSON的结果, 这或这也没有帮助我。
更新:看起来更像它: http : //geekswithblogs.net/DavidHoerster/archive/2011/07/26/json.net-custom-convertersndasha-quick-tour.aspx
解决这个问题的更简单的方法是使用JSON .NET中的JsonExtensionDataAttribute
public class MyClass { // known field public decimal TaxRate { get; set; } // extra fields [JsonExtensionData] private IDictionary<string, JToken> _extraStuff; }
这里有一个项目博客的例子
更新请注意,这需要JSON .NET v5版本5及更高版本
见https://gist.github.com/LodewijkSioen/5101814
你在找什么是一个自定义的JsonConverter
这是你可以解决的一个方法,虽然我不喜欢那么多。 我使用Newton / JSON.Net解决了这个问题。 我想你也可以使用JsonConverter进行反序列化。
private const string Json = "{\"id\":7908,\"name\":\"product name\",\"_unknown_field_name_1\":\"some value\",\"_unknown_field_name_2\":\"some value\"}"; [TestMethod] public void TestDeserializeUnknownMembers() { var @object = JObject.Parse(Json); var serializer = new Newtonsoft.Json.JsonSerializer(); serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Error; serializer.Error += (sender, eventArgs) => { var contract = eventArgs.CurrentObject as Contract ?? new Contract(); contract.UnknownValues.Add(eventArgs.ErrorContext.Member.ToString(), @object[eventArgs.ErrorContext.Member.ToString()].Value<string>()); eventArgs.ErrorContext.Handled = true; }; using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(Json))) using (StreamReader streamReader = new StreamReader(memoryStream)) using (JsonReader jsonReader = new JsonTextReader(streamReader)) { var result = serializer.Deserialize<Contract>(jsonReader); Assert.IsTrue(result.UnknownValues.ContainsKey("_unknown_field_name_1")); Assert.IsTrue(result.UnknownValues.ContainsKey("_unknown_field_name_2")); } } [TestMethod] public void TestSerializeUnknownMembers() { var deserializedObject = new Contract { id = 7908, name = "product name", UnknownValues = new Dictionary<string, string> { {"_unknown_field_name_1", "some value"}, {"_unknown_field_name_2", "some value"} } }; var json = JsonConvert.SerializeObject(deserializedObject, new DictionaryConverter()); Console.WriteLine(Json); Console.WriteLine(json); Assert.AreEqual(Json, json); } } class DictionaryConverter : JsonConverter { public DictionaryConverter() { } public override bool CanConvert(Type objectType) { return objectType == typeof(Contract); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var contract = value as Contract; var json = JsonConvert.SerializeObject(value); var dictArray = String.Join(",", contract.UnknownValues.Select(pair => "\"" + pair.Key + "\":\"" + pair.Value + "\"")); json = json.Substring(0, json.Length - 1) + "," + dictArray + "}"; writer.WriteRaw(json); } } class Contract { public Contract() { UnknownValues = new Dictionary<string, string>(); } public int id { get; set; } public string name { get; set; } [JsonIgnore] public Dictionary<string, string> UnknownValues { get; set; } }
}
因为最近我也有类似的问题,所以我觉得我会把自己的帽子戴在戒指上。 这是我想要反序列化的JSON的一个例子:
{ "agencyId": "agency1", "overrides": { "assumption.discount.rates": "value: 0.07", ".plan": { "plan1": { "assumption.payroll.growth": "value: 0.03", "provision.eeContrib.rate": "value: 0.35" }, "plan2": { ".classAndTier": { "misc:tier1": { "provision.eeContrib.rate": "value: 0.4" }, "misc:tier2": { "provision.eeContrib.rate": "value: 0.375" } } } } } }
这是一个覆盖不同层次并在树上inheritance的系统。 在任何情况下,我想要的数据模型都可以让我拥有一个带有这些特殊inheritance规则的属性包。
结果是:
public class TestDataModel { public string AgencyId; public int Years; public PropertyBagModel Overrides; } public class ParticipantFilterModel { public string[] ClassAndTier; public string[] BargainingUnit; public string[] Department; } public class PropertyBagModel { [JsonExtensionData] private readonly Dictionary<string, JToken> _extensionData = new Dictionary<string, JToken>(); [JsonIgnore] public readonly Dictionary<string, string> Values = new Dictionary<string, string>(); [JsonProperty(".plan", NullValueHandling = NullValueHandling.Ignore)] public Dictionary<string, PropertyBagModel> ByPlan; [JsonProperty(".classAndTier", NullValueHandling = NullValueHandling.Ignore)] public Dictionary<string, PropertyBagModel> ByClassAndTier; [JsonProperty(".bargainingUnit", NullValueHandling = NullValueHandling.Ignore)] public Dictionary<string, PropertyBagModel> ByBarginingUnit; [OnSerializing] private void OnSerializing(StreamingContext context) { foreach (var kvp in Values) _extensionData.Add(kvp.Key, kvp.Value); } [OnSerialized] private void OnSerialized(StreamingContext context) { _extensionData.Clear(); } [OnDeserialized] private void OnDeserialized(StreamingContext context) { Values.Clear(); foreach (var kvp in _extensionData.Where(x => x.Value.Type == JTokenType.String)) Values.Add(kvp.Key, kvp.Value.Value<string>()); _extensionData.Clear(); } }
基本的想法是这样的:
- 通过JSON.NET进行反序列化的PropertyBagModel具有填充的ByPlan,ByClassAndTier等字段,并且还具有填充的专用_extensionData字段。
- 然后,JSON.NET调用私有的OnDeserialized()方法,并将数据从_extensionData移动到适当的值(或者放在地板上,否则可能会logging下来,如果这是你想知道的东西)。 然后,我们从_extensionData中删除额外的垃圾,所以它不消耗内存。
- 在序列化时,OnSerializing方法获取调用,将东西移动到_extensionData中,以便保存。
- 序列化完成后,OnSerialized被调用,我们从_extensionData中删除额外的东西。
我们可以在需要的时候进一步删除和重新创build_extensionData字典,但是我没有看到真正的值,因为我没有使用这些对象。 为此,我们只需在OnSerializing上创build并在OnSerialized上删除即可。 关于反序列化,我们可以释放它,而不是清除。