如何在referer上将引用的对象展平为两个json.net属性?

如何在referer上将引用的对象展平为两个json.net属性?,.net,serialization,json.net,.net,Serialization,Json.net,考虑以下类别: public class User { public virtual int Id {get;set;} public virtual string Name {get;set;} public virtual User Superior {get;set;} } 我的目标是使用newtonsofts json.net将其序列化为json,如下所示: { Id: 101, Name: 'Mithon', SuperiorId: 100, Superi

考虑以下类别:

public class User
{
  public virtual int Id {get;set;}
  public virtual string Name {get;set;}
  public virtual User Superior {get;set;}
}
我的目标是使用newtonsofts json.net将其序列化为json,如下所示:

{
  Id: 101,
  Name: 'Mithon',
  SuperiorId: 100,
  SuperiorName: 'TheMan'
}
我为什么要这样做?因为我希望使用Json作为DTO,而不生成动态对象的中间层。生成DTO应该按照约定动态地进行,而不是显式地进行,imho。我知道有些人可能强烈反对这一点,但讨论我的方法并不是重点。我只是想知道这是否能做到,以及如何做到

挑战在于,对Superior属性使用
JsonPropertyAttribute
只会产生一个属性作为输出,而我需要两个。如果我使用
JsonObjectAttribute
我将得到一个嵌套的属性,并且顶级用户也会被压扁,这会带来麻烦


幸运的是,json.net库中似乎有足够的受保护和/或公共属性和方法,我可以扩展一些内容以获得所需的结果。接下来的问题是,我应该从哪些类和方法开始,以达到我想要的目的?从DefaultContractResolver派生并重写GetProperties方法是一个好地方,还是我应该去别处看看?

简单的回答是肯定的,这是一个合适的起点。以下是我(目前)的结局:

很遗憾,我的干净实体和动态序列化的所有血淋淋的细节都被封装在两个地方,但这并没有帮助,因为我不想从序列化程序访问我的数据源

public class MyContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);

        foreach (var pi in type.GetProperties().Where(pi => typeof (Entity).IsAssignableFrom(pi.DeclaringType) && Attribute.IsDefined((MemberInfo) pi, typeof(IdNameMemberAttribute))))
        {
            properties.Add(CreateReferenceProperty(pi, Reflect.GetProperty<Entity>(e => e.Id)));
            properties.Add(CreateReferenceProperty(pi, Reflect.GetProperty<Entity>(e => e.Name)));
        }

        return properties;
    }

    private JsonProperty CreateReferenceProperty(PropertyInfo reference, PropertyInfo referenceMember)
    {
        var jsonProperty = base.CreateProperty(reference, MemberSerialization.OptOut);
        jsonProperty.PropertyName += referenceMember.Name;
        jsonProperty.ValueProvider = new ReferencedValueProvider(reference, referenceMember);
        jsonProperty.Writable = false;

        return jsonProperty;
    }
}
var jsonSource = streamReader.ReadToEnd();
var deserializedObject = JsonConvert.DeserializeObject(jsonSource, type, SerializerSettings);
var changeSet = deserializedObject as PropertyChangeSet;
if (changeSet != null)
{
    var jsonChange = JObject.Parse(jsonSource)["Change"].Cast<JProperty>().ToArray();

    IDictionary<string, int> references = jsonChange.Where(IsReferenceId).ToDictionary(t => t.Name.Substring(0, t.Name.Length - 2), t => t.Value.ToObject<int>());
    changeSet.References = references;

    var properties = jsonChange.Where(jp => !IsReferenceId(jp)).Select(t => t.Name).ToList();
    changeSet.Primitives = properties;
}