Assuming your object structure (in an external dll) cannot be modified in any way, you can still create the required JSON using custom JsonConverter , which internally uses custom ContractResolver to create a list of public and private fields at each level of the type hierarchy with corresponding get and set methods:
public class DeclaredFieldJsonConverter<T> : JsonConverter where T: new() { const string basePropertyName = "base"; public override bool CanConvert(Type objectType) { return typeof(T).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var jObj = JObject.Load(reader); existingValue = existingValue ?? new T(); var type = existingValue.GetType(); while (jObj != null && type != null) { var basejObj = jObj.ExtractPropertyValue(basePropertyName) as JObject; JsonObjectContract contract = (JsonObjectContract)DeclaredFieldContractResolver.Instance.ResolveContract(type); foreach (var jProperty in jObj.Properties()) { var property = contract.Properties.GetClosestMatchProperty(jProperty.Name); if (property == null) continue; var value = jProperty.Value.ToObject(property.PropertyType, serializer); property.ValueProvider.SetValue(existingValue, value); } type = type.BaseType; jObj = basejObj; } return existingValue; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { WriteJson(writer, value, value.GetType(), serializer); } void WriteJson(JsonWriter writer, object value, Type type, JsonSerializer serializer) { JsonObjectContract contract = (JsonObjectContract)DeclaredFieldContractResolver.Instance.ResolveContract(type); writer.WriteStartObject(); foreach (var property in contract.Properties.Where(p => !p.Ignored)) { writer.WritePropertyName(property.PropertyName); serializer.Serialize(writer, property.ValueProvider.GetValue(value)); } var baseType = type.BaseType; if (baseType != null && baseType != typeof(object)) { writer.WritePropertyName(basePropertyName); WriteJson(writer, value, baseType, serializer); } writer.WriteEndObject(); } } public static class JsonExtensions { public static JToken ExtractPropertyValue(this JObject obj, string name) { if (obj == null) return null; var property = obj.Property(name); if (property == null) return null; var value = property.Value; property.Remove(); property.Value = null; return value; } } class DeclaredFieldContractResolver : DefaultContractResolver {
Then use it as follows:
var demo = new MyDemo(); var json = JsonConvert.SerializeObject(demo, new DeclaredFieldJsonConverter<MyDemo>());
Fiddle example.
Please note that if the type hierarchy has a field named base , duplicate JSON property names will be written, which will lead to potential loss of information during deserialization. You can check this out and process it in some way.
source share