自定义成员的运行时序列化

本文关键字:序列化 运行时 成员 自定义 | 更新日期: 2023-09-27 18:26:56

我想知道是否有人能为我指明正确的方向,让我轻松解决以下问题:

假设我的.NET代码中有一个Player类,它看起来像这样:

public class Player
{
   public int Id { get; set; }
   public string Name { get; set; }
   public long Score { get; set; }
}

我需要将这个类序列化为JSON字符串(使用JSON.NET),并在POST到web服务时使用它。然而,问题是服务的一些端点在JSON字符串中明确禁止某些成员的出现。例如,一个"post-score"端点将允许所有3个成员都包含在字符串中,而一个"register-player"端点将只允许Id和Name存在(否则,一个错误的请求将被抛出给客户端)。现在我知道我可以创建两个不同的类(例如Player和CompetitivePlayer),每个类都包含所需的(子)成员集,但出于实际目的,让我们假设我不能这样做或想避免这样做(我的实际数据对象比这里举的Player类更复杂)。

因此,我实际想要的是在运行时告诉JSON序列化程序,在情况X中,只有对象的某些成员必须被序列化,而在情况Y中,将序列化一个完全不同的子集。起初,我认为实现我自己的ContractResolver会有所帮助,但事实证明,在序列化它时,每个对象的类型只调用一次,而不是每个对象本身。现在,我能想到的唯一解决方案是将JSONSerializer子类化,并使其使用JSONWriter,该JSONWriter忽略名称包含在作为参数的字符串列表中的属性,也许吧——尽管我不太确定这个计划是否可行。对于我想要实现的目标,有没有一个更简单的解决方案?

自定义成员的运行时序列化

好的,在查看了JSON.NET源代码后,我发现了阻止我自定义序列化这些属性的确切位置:CamelCasePropertyNamesContractResolver类。

在写最初的问题之前,我尝试实现这里IContractResolver部分所描述的自定义序列化。然而,我没有直接从DefaultContractResolver继承,而是使用了CamelCasePropertyNamesContractResolver(这里我需要camel-case),在查看其代码内部后,它设置了一个"共享缓存"标志,出于性能原因(我愿意在这种情况下牺牲这一点),该标志阻止了它的一些方法被调用。因此,每个对象类型只调用CreateProperties()一次,而不是每次需要序列化我的对象时调用。所以现在我的合约解析器类看起来是这样的:

class OptionalPropertiesContractResolver : DefaultContractResolver
{
    //only the properties whose names are included in this list will be serialized
    IEnumerable<string> _includedProperties;
    public OptionalPropertiesContractResolver(IEnumerable<string> includedProperties)
    {
        _includedProperties = includedProperties;
    }
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        return (from prop in base.CreateProperties(type, memberSerialization)
                where _includedProperties.Contains(prop.PropertyName)
                select prop).ToList();
    }
    protected override string ResolvePropertyName(string propertyName)
    {
        // lower case the first letter of the passed in name
        return ToCamelCase(propertyName);
    }
    static string ToCamelCase(string s)
    {
        //camel case implementation
    }
}

只是想让其他人知道这个特殊的情况,以防他们遇到它。

我会创建契约类,并使用AutoMapper从Player类映射到它们,并根据需要对它们进行序列化。即"玩家合同"、"竞争性玩家合同"等

这些类只代表服务的契约并不重要。