【问题标题】:Copy JsonSerializerSettings from JsonSerializer to new JsonSerializer将 JsonSerializerSettings 从 JsonSerializer 复制到新的 JsonSerializer
【发布时间】:2016-07-06 17:39:35
【问题描述】:

有没有办法将设置从JsonSerializer 类中取出并在新的JsonSerializer 中重新实现?

似乎没有任何方法可以做这样的事情。我发现最好的方法是通过反射调用私有方法ApplySerializerSettings。

我正在尝试从WriteJson 方法中获取序列化程序并将其复制到一个新的序列化程序中,并对其进行一些调整。具体来说,我想替换ContractResolver。

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)

【问题讨论】:

    标签: c# serialization json.net


    【解决方案1】:

    似乎最好的方法是将所有设置复制到一个新对象中。它们有很多,所以这是一个很好的扩展方法(从 8.0.3 开始)。

    public static class JsonSerializerExtensions
    {
      public static JsonSerializer DeepCopy(this JsonSerializer serializer)
      {
        var copiedSerializer = new JsonSerializer
        {
          Context = serializer.Context,
          Culture = serializer.Culture,
          ContractResolver = serializer.ContractResolver,
          ConstructorHandling = serializer.ConstructorHandling,
          CheckAdditionalContent = serializer.CheckAdditionalContent,
          DateFormatHandling = serializer.DateFormatHandling,
          DateFormatString = serializer.DateFormatString,
          DateParseHandling = serializer.DateParseHandling,
          DateTimeZoneHandling = serializer.DateTimeZoneHandling,
          DefaultValueHandling = serializer.DefaultValueHandling,
          EqualityComparer = serializer.EqualityComparer,
          FloatFormatHandling = serializer.FloatFormatHandling,
          Formatting = serializer.Formatting,
          FloatParseHandling = serializer.FloatParseHandling,
          MaxDepth = serializer.MaxDepth,
          MetadataPropertyHandling = serializer.MetadataPropertyHandling,
          MissingMemberHandling = serializer.MissingMemberHandling,
          NullValueHandling = serializer.NullValueHandling,
          ObjectCreationHandling = serializer.ObjectCreationHandling,
          PreserveReferencesHandling = serializer.PreserveReferencesHandling,
          ReferenceResolver = serializer.ReferenceResolver,
          ReferenceLoopHandling = serializer.ReferenceLoopHandling,
          StringEscapeHandling = serializer.StringEscapeHandling,
          TraceWriter = serializer.TraceWriter,
          TypeNameHandling = serializer.TypeNameHandling,
          SerializationBinder = serializer.SerializationBinder,
          TypeNameAssemblyFormatHandling = serializer.TypeNameAssemblyFormatHandling
        };
        foreach (var converter in serializer.Converters)
        {
          copiedSerializer.Converters.Add(converter);
        }
        return copiedSerializer;
      }
    }
    

    它很丑,但至少你只需要写一次。 请稍微小心,因为属性本​​身不会被深度复制。

    根据您的实施情况,下面的答案很不稳定,尤其是在解决合同时。保留它以防万一它对某人有帮助。


    所以,我不能完全复制这些设置,但我找到了一个很好的解决方法,可能需要考虑。您可以在锁定的上下文中简单地设置要更改的属性,然后再将它们重置。

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var thing = value as IThing;
    
        if (thing == null)
            throw new ArgumentException($"Writing Json failed because " + 
              "value was not a 'Thing' of type, {typeof(IThing).FullName}");
    
        JObject jsonThing;
        //If your solution is multithreaded,
        //and is using a shared serializer (which you probably are),
        //you should lock the serializer so that it doesn't accidentally use 
        //the "CustomObjectResolver"
        lock (serializer)
        {
            //Hold the original value(s) to reset later
            var originalContractResolver = serializer.ContractResolver;
            //Set custom value(s)
            serializer.ContractResolver = new CustomObjectResolver();
            //Serialization with custom properties
            jsonThing = JObject.FromObject(value, serializer);
            //Reset original value(s)
            serializer.ContractResolver = originalContractResolver;
        }
    
        //Finish serializing and write to writer.
    }
    

    【讨论】:

    • 谢谢,省去我打字的麻烦!有点恶心,我们必须这样做,但确实应该在 Newtonsoft.Json 中内置一些东西
    【解决方案2】:

    不确定这是否是主题 - 但由于这是在我的 Google 中搜索 Clone JSONSerializerSettings 时出现的第一个帖子,我想我会留下一个通知。

    我刚刚有一个类似的要求。

    在我的 .Net Core WebApi 解决方案中,我们有一个用于序列化 JSON 的默认设置,使用

    jsonSerializaerSettings.NullValueHandling = NullValueHandling.Ignore;
    

    但在一些端点中,我也想序列化空属性,因为这些端点代表一个数据集。所以所有对象在序列化时都应该具有所有属性。

    我尝试了一些方法,然后阅读了 Options 模式,我偶然发现了 IOptionSnapshot - https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.1#reload-configuration-data-with-ioptionssnapshot

    所以我最终将一个 IOptionsSnapshot 注入到我的控制器方法中,这为我提供了默认选项的副本,我可以对其进行修改并用于单个请求。

    在下一次请求时,注入的选项将恢复为默认值。

    很好很简单——至少对我来说是这样。

    所以我的样本的最终解决方案是这样的

    [HttpPost("dataset", Name = "GetReportDataset")]
    public IActionResult GetReportDataset(
        [FromServices]IOptionsSnapshot<MvcJsonOptions> jsonOptions,
        [FromBody]DataSetReport report,
        [FromQuery]int? top)
    {
        //Logic to fetch dataset
        var myDataSet = myRepository.GetDataset(report, top);
    
        var serializerSettings = jsonOptions.Value.SerializerSettings;
        serializerSettings.NullValueHandling = NullValueHandling.Include;
    
        return new JsonResult(myDataSet, serializerSettings);
    }
    

    希望这可以帮助其他人来这里进行类似的使用。

    /安德斯

    【讨论】:

    • 对于 .NET Core 这应该是答案。我遇到了完全相同的场景,其中 99% 的序列化发生在默认设置下,但有一个端点我希望有一个不同的选项。
    • 是的,它有效。顺便说一句,他们添加了一个复制 ctor github.com/dotnet/runtime/issues/30445
    • 但最初的问题是关于Newtonsoft.Json 你描述的是System.Text.Json 的解决方案
    猜你喜欢
    • 1970-01-01
    • 2021-08-24
    • 2014-07-16
    • 2020-03-20
    • 2013-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-14
    相关资源
    最近更新 更多