111

I'm using json.net (Newtonsoft's JsonSerializer). I need to customize serialization in order to meet following requirements:

  1. property names must start with lower case letter.
  2. Dictionary<string, T> must be serialized into jsonp where keys will be used for property names. LowerCase rule does not apply for dictionary keys.

for example:

var product = new Product();
procuct.Name = "Product1";
product.Items = new Dictionary<string, Item>();
product.Items.Add("Item1", new Item { Description="Lorem Ipsum" });

must serialize into:

{
  name: "Product1",
  items : {
    "Item1": {
       description : "Lorem Ipsum"
    }
  }
}

notice that property Name serializes into "name", but key Item1 serializes into "Item1";

I have tried to create CustomJsonWriter to serialize property names, but it changes also dicionary keys.

public class CustomJsonWriter : JsonTextWriter
{
    public CustomJsonWriter(TextWriter writer) : base(writer)
    {
        
    }
    public override void WritePropertyName(string name, bool escape)
    {
        if (name != "$type")
        {
            name = name.ToCamelCase();
        }
        base.WritePropertyName(name, escape);
    }
}

EDIT: the best answer is using Naming Strategy as @alelom suggested in comments:

new CamelCaseNamingStrategy
{
    ProcessDictionaryKeys = false
}
Liero
  • 25,216
  • 29
  • 151
  • 297
  • 1
    The right answer to this question is here: https://stackoverflow.com/a/40732416/3873799. It involves using the [`NamingStrategy`](https://www.newtonsoft.com/json/help/html/NamingStrategySkipDictionaryKeys.htm) to handle these cases. It exposes granular options that you can use to camelCase the serialisation, without having to pollute the class with attribute tags, or risking applying camelCasing in the wrong places. – alelom Apr 13 '22 at 10:56

2 Answers2

193

You could try using the CamelCasePropertyNamesContractResolver.

var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
var json = JsonConvert.SerializeObject(product, serializerSettings);

I'm just not sure how it'll handle the dictionary keys and I don't have time right this second to try it. If it doesn't handle the keys correctly it's still worth keeping in mind for the future rather than writing your own custom JSON writer.

Craig W.
  • 17,838
  • 6
  • 49
  • 82
  • 18
    Be careful with this option. This option cause lowercasing keys of the serialized dictionaries as well. This is usually is not what is desired. – user1936595 Mar 06 '18 at 02:16
  • 3
    This question's answer tells how you can implement your own ContractResolver for example for using lower case. https://stackoverflow.com/questions/6288660/ensuring-json-keys-are-lowercase-in-net/6288726 – Mark Baijens Sep 24 '18 at 09:42
  • 16
    If you only want the casing to apply to one object you can use: `var json = JsonConvert.SerializeObject(product, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Formatting.Indented });` – PutoTropical Jan 25 '19 at 17:16
  • 5
    The simplest and most reliable solution is using the [`NamingStrategy`](https://www.newtonsoft.com/json/help/html/NamingStrategySkipDictionaryKeys.htm) to handle these cases. It exposes granular options that you can use to camelCase the serialisation, without having to pollute the class with attribute tags, or risking applying camelCasing in the wrong places. – alelom Apr 13 '22 at 10:57
75

You can use a JsonProperty to change how something is serialized/deserialized. When you define your object add the property items to the fields you would like represented differently in the JSON. This only works with NewtonsoftJSON. Other libraries may do it differently.

public class Product
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("items")]
    public Dictionary<string, Item> Items { get; set; }
}

public class Item
{
    [JsonProperty("description")]
    public string Description { get; set; }
}
Brian from state farm
  • 2,825
  • 12
  • 17
  • 14
    This is not a violation. It provides a mapping which may be necessary when dealing with data serialization. This is due to mismatch between C# variable names and the underlying serialization format - not limited to JSON. – Metro Feb 12 '17 at 16:28
  • 2
    well, if there's a rule or convention that can be used to determine the mapping, then it is violation of DRY – Liero Jul 08 '17 at 16:53
  • The DRY principle is stated as "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. Modification of any single element of a system does not require a change in other logically unrelated elements. Additionally, elements that are logically related all change predictably and uniformly, and are thus kept in sync." – Liero Jul 08 '17 at 17:00
  • 6
    In this case the knowledge is property name and the convention used for serialization. But since you specified both property name and related serialized property name, they are not kept in sync. In your example, changing one element requires (property name) requires change to the other element (serialized property name). Clearly a violation of DRY – Liero Jul 08 '17 at 17:01
  • 1
    @Liero Seeing how it's from 5 years ago, you probably learned this by now - but that quote has been applied exactly the wrong way around. If you only rename a property in C#, you don't want that to _"require a change in other logically unrelated elements"_ like e.g. the data contract with your JS frontend. After all, renaming a property falls under "refactoring", which means not changing the behaviour of the code. – Raphael Schmitz Nov 26 '21 at 18:00
  • I don't see it as a violation, if it were then that'd mean all DTOs are a violation of DRY? Mapping "Name" to "name" and back again is just like a DTO. I could rename the "Name" property to "FooBar" and it'd still map to "name" and back to "FooBar" - `Every piece of knowledge must have a single, unambiguous, authoritative representation within a system` so it all depends on where your system ends... I'm serialising stuff in my system so it can communicate with another system... – jamheadart Feb 08 '22 at 14:45
  • @jamheadart: __"I could rename the "Name" property to "FooBar" and it'd still map to "name" and back to "FooBar""__. Exactly! Your classes have different responsibilities, different reason to change. In my case, when I rename property I want that to be reflected in JSON. But yeah, definition of DRY is vague and we must often do tradeoffs between DRY a SoC for example. – Liero Aug 31 '22 at 06:56
  • Attributes are a shit-show. Attributing with `JsonProperty` couples your class tightly to the Newtonsoft library, thus no longer making it a POCO. What happens if I decide to change the json serialiser/deserialiser being used? Do I go through each and every class changing all references to `JsonProperty`!? – Ash Jul 26 '23 at 09:33