如何将引用的对象平面化为对引荐2 json.net属性?如何将、属性、平面、对象

2023-09-03 01:27:34 作者:妖精看哥收了你!

考虑下面的类:

public class User
{
  public virtual int Id {get;set;}
  public virtual string Name {get;set;}
  public virtual User Superior {get;set;}
}

我的目标是使用序列化此为JSON newtonsofts json.net像这样:

My goal is to serialize this as json using newtonsofts json.net like so:

{
  Id: 101,
  Name: 'Mithon',
  SuperiorId: 100,
  SuperiorName: 'TheMan'
}

我为什么要这么做?因为我想使用JSON作为我的DTO的,不产生动态物体的中间层。生成DTO的动态应按照约定来完成,而不是明确的,恕我直言。我知道有些人可能会极力与此不同意,但在讨论我的做法是除了点。我只是想知道是否和如何是可以做到的。

Why do I want to do this? Because I want to use the Json as my DTO's without generating an intermediate layer of dynamic objects. Generating the DTO's should be done dynamically by convention rather than explicitly, imho. I know some might strongly disagree with this, but discussing my approach is besides the point. I just want to know if and how it can be done.

目前的挑战是,使用 JsonPropertyAttribute 的高级属性将产生只有一个属性作为输出,在这里我需要两个。如果我用 JsonObjectAttribute 我会得到一个嵌套的属性,我会与顶层用户也被夷为平地的麻烦。

The challenge is that using JsonPropertyAttribute for the Superior property will yield only one property as output, where I need two. If I use a JsonObjectAttribute I will get a nested attribute and I would have trouble with the top level User also being flattened.

幸运的是,似乎有在json.net库,我可以扩展的东西得到期望的结果足够的保护和/或公共的属性和方法。接下来的问题是我应该开始用得到我想要去哪个类和方法?会派生从DefaultContractResolver,并重写getProperties方法是很好的地方,或者我应该看看其他地方?

Luckily it seems there are enough protected and/or public properties and methods in the json.net library that I can extend something to get the desired result. The question then is which classes and methods should I start with to get where I want to go? Would deriving from DefaultContractResolver, and overriding the GetProperties method be good places, or should I look elsewhere?

推荐答案

简短的回答是肯定的,那将是适当的地方开始。这里是我结束了(现在):

The short answer is yes, that would be appropriate places to start. Here's what I ended up with (for now):

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。

IdNameMemberAttribute is just an empty attribute that I use to annotate which reference properties I want to serialize. The important bit is that I do NOT annotate it with anything that Json.NET will recognize and use to generate a JsonProperty. That way I don't end up with a duplicate JsonProperty from my CreateProperties.

另外,我可以来源于DataMemberAttribute和寻找,修改和克隆JsonProperty重新present我的编号名称

Alternatively I could have derived from DataMemberAttribute and looked for, modified and cloned the JsonProperty to represent my Id and Name.

有关我的asp.net的web API,我则可以用这个MyContractResolver为JsonFormatter.SerializerSettings的ContractResolver。

For my asp.net web api I then set this MyContractResolver as the ContractResolver of JsonFormatter.SerializerSettings.

这样的固定我进行序列化。对于反序列化我有我的存储的PropertyInfo和对象自定义的变更对象。在反序列化然后我要确保我使用自定义ActionFilter与访问数据存储会话保持IDS,后来解决这些从我的数据存储,在我的情况。

So that fixed me up for serialization. For deserialization I have a custom ChangeSet object where I store PropertyInfo and objects. During deserialization I then make sure I keep the Ids, and later resolve those from my data store, in my case using custom ActionFilter with access to the data store session.

下面是我的序列化的本质是:

Here is the essence of my serialization:

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;
}

和preSTO,我的干净的实体和动态序列化的复杂细节被封装,遗憾的是在两个地方,但它不能得到帮助,因为我不希望从串行访问我的数据源。

And presto, all the gory details of my clean entities and dynamic serialization are encapsulated, sadly in two places, but it couldn't be helped since I don't want to access my data source from the serializer.