0

I'm currently working on a big API Project and my task is, to make our JSON-Output canonical (No Whitespaces/Linebrakes and keys/values sorted in alphabetical order).

I've already managed to override the ContractResolver to sort the keys alphabetically, but its still missing the ordering of the values. The whitespaces get removed already and the other canonical factors are already done.

      protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
    }

My two questions are:

  1. Where to I apply the overwritten ContractResolver, so that it actually applies? I've already added it to the startup like this, but it doesnt affect my output.

       services.AddControllers()
             .AddNewtonsoftJson(options =>
             {
                 options.SerializerSettings.ContractResolver = new CanonicalContractResolver();
             });
    

    or like this in my TestMethod, but it still doesnt work.

     public void writeCanonicalJsonTest_Ok()
     {
         Assert.IsNotNull(policyConverter);
    
         var policy = new PrivacyPolicy()
         {
             Version = "1.2",
             Languages = new Languages()
             {
                 LanguageList = new List<Language>()
                 {
                     new Language()
                     {
                         LanguageOption = LanguageOption.En,
                         Name = "Terms of Service",
                         Url = "https://example.org/somewhere/terms-2.0-en.html"
                     },
                     new Language()
                     {
                         LanguageOption = LanguageOption.Fr,
                         Name = "Conditions d'utilisation",
                         Url = "https://example.org/somewhere/terms-2.0-fr.html"
                     }
                 }
             }
         };
    
         var settings = new JsonSerializerSettings();
         settings.Converters.Add(new PolicyConverter());
         settings.ContractResolver = new CanonicalContractResolver();
    
         var result = JsonConvert.SerializeObject(policy, typeof(Policy), settings);
         var test = JsonConvert.DeserializeObject<Policy>(result, settings);
    
         var noWhite = "{\"en\":{\"name\":\"Terms of Service\",\"url\":\"https://example.org/somewhere/terms-2.0-en.html\"},\"fr\":{" +
             "\"name\":\"Conditions d'utilisation\",\"url\":\"https://example.org/somewhere/terms-2.0-fr.html\"},\"version\":\"1.2\"}}";
    
         if (test == null)
         {
             throw new ArgumentNullException("Result must have a value");
         }
         Assert.IsNotNull(test);
         Assert.AreEqual(policy.Version, test.Version);
         Assert.IsNotNull(test.Languages);
         Assert.IsNotNull(test.Languages.LanguageList);
         Assert.AreEqual(policy.Languages.LanguageList[0].LanguageOption, test.Languages.LanguageList[0].LanguageOption);
         Assert.AreEqual(policy.Languages.LanguageList[0].Name, test.Languages.LanguageList[0].Name);
         Assert.AreEqual(policy.Languages.LanguageList[0].Url, test.Languages.LanguageList[0].Url);
         Assert.AreEqual(policy.Languages.LanguageList[1].LanguageOption, test.Languages.LanguageList[1].LanguageOption);
         Assert.AreEqual(policy.Languages.LanguageList[1].Name, test.Languages.LanguageList[1].Name);
         Assert.AreEqual(policy.Languages.LanguageList[1].Url, test.Languages.LanguageList[1].Url);
         Assert.AreEqual(noWhite, result);
     }
    
  2. How do I additionally to sorting by keys, also sort by values?

Those two are actually so easy, but the documentation of newtonsoft is very sparce and I cant find any solutions after more then 8hrs of searching - really frustrating.

Thanks in advance for your help!

DennisM
  • 17
  • 7
  • 1) What do you mean by *sort by values?*? [RFC 8785 JSON Canonicalization Scheme (JCS)](https://www.rfc-editor.org/rfc/rfc8785#name-sorting-of-object-propertie) only specifies sorting of property names. Array values are not to be sorted. 2) *or like this in my TestMethod, but it still doesnt work.* -- can you share a [mcve] that includes definitions of `PrivacyPolicy` and `Language`? – dbc Sep 21 '22 at 16:15
  • 1
    Incidentally, `AddNewtonsoftJson()` only applies when the framework is serializing for you automatically, e.g. by doing `return new PrivacyPolicy { /* Contents here */ }` from some web API method. It doesn't influence direct calls. For direct calls passing in the serializer settings should work -- which is why I would request a [mcve] for case 2. See also [Json.net global settings](https://stackoverflow.com/a/44642045). – dbc Sep 21 '22 at 16:23
  • Your `CanonicalContractResolver` doesn't by any chance inherit from `CamelCasePropertyNamesContractResolver`, does it? – dbc Sep 21 '22 at 16:48
  • 1
    I tried your `CanonicalContractResolver` and it seems to work, see https://dotnetfiddle.net/il5FgA. One note: you should sort using `StringComparer.Ordinal` to avoid locale-dependent sorting. – dbc Sep 21 '22 at 16:59

0 Answers0