.net - 如何将引用的对象展平为引用者上的两个 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,
  SuperiorName: 'TheMan'
}

我为什么要这样做?因为我想使用 Json 作为我的 DTO,而不生成动态对象的中间层。生成 DTO 应该按照约定动态完成,而不是明确地,恕我直言。我知道有些人可能会强烈反对这一点,但讨论我的方法并不是重点。我只是想知道是否以及如何做到这一点。

挑战在于使用 JsonPropertyAttribute因为 Superior 属性只会产生一个属性作为输出,而我需要两个。如果我使用 JsonObjectAttribute我将获得一个嵌套属性,并且顶级用户也被展平会遇到麻烦。

幸运的是,在 json.net 库中似乎有足够的 protected 和/或公共(public)属性和方法,我可以扩展一些东西以获得所需的结果。那么问题是我应该从哪些类和方法开始到达我想去的地方?从 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;
    }
}
IdNameMemberAttribute只是一个空属性,我用它来注释我想要序列化的引用属性。重要的是,我不会使用 Json.NET 将识别并用于生成 JsonProperty 的任何内容对其进行注释。这样我就不会从我的 CreateProperties 中得到一个重复的 JsonProperty。

或者,我可以从 DataMemberAttribute 派生并查找、修改和克隆 JsonProperty 来代表我的 IdName .

然后,对于我的 asp.net web api,我将此 MyContractResolver 设置为 JsonFormatter.SerializerSettings 的 ContractResolver。

所以这让我为序列化做好了准备。对于反序列化,我有一个自定义 ChangeSet 对象,我在其中存储 PropertyInfo 和对象。然后,在反序列化期间,我确保保留 Id,然后从我的数据存储中解析它们,在我的情况下,使用自定义 ActionFilter 访问数据存储 session 。

这是我的序列化的精髓:
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;
}

而且,我的干净实体和动态序列化的所有血淋淋的细节都被封装了,遗憾的是在两个地方,但由于我不想从序列化程序访问我的数据源,这无济于事。

关于.net - 如何将引用的对象展平为引用者上的两个 json.net 属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15333820/

相关文章:

C# 创建类和派生类的实例

c# - 为什么 "1.0"< "-1.0"< "1.1"?

c# - 是否可以将 Cortana 用于应用内语音命令?

Java/可序列化 - 仅覆盖已更改的对象

c# - Json.Net 结合序列化的 2 个属性

c# - C# 中带标志的 cURL 调用

java - 测试实现 java.lang.Serializable 的类以确保任何更改都向后兼容

json - 使用 C# 中的 JSON.NET 反序列化 Twitter JSON 以获取主题标签

c# - 使用 JsonConvert.DeserializeObject 反序列化派生对象列表

c#-4.0 - 如何从 JSON 字符串中删除转义字符