0

In an object I am serializing to a document database I have a dynamic property that needs all string values written in lower-case for indexing purposes. I tried using a custom converter for this, but that requires me to write serialization code for several different types. All I want is the standard serialization behavior, but with the string values forced to lower-case. Given the flexibility of the Newtonsoft library this seems like it should be straightforward, I just haven't found the right interface.

Note that I don't have direct control over serialization. I am writing to Azure COSMOSDB, and their client library uses JSON.Net for serialization. I am able to pass in a custom serializer to use.

Update

The following appears to work. Steps are:

  1. add code to populate the dynamic property via JToken.FromObject()
  2. use code below to force all string values to lower-case (arrays were tricky)
  3. send object to CosmosDB

    private static JToken JTokenStringValuesToLower(JToken startToken)
    {
        Stack<JEnumerable<JToken>> tokenStack = new Stack<JEnumerable<JToken>>();
        tokenStack.Push(startToken.Children());
    
        while (tokenStack.Count != 0)
        {
            JEnumerable<JToken> children = tokenStack.Pop();
    
            foreach (JToken child in children)
            {
                if (child.Type == JTokenType.Property)
                {
                    JProperty property = (JProperty)child;
    
                    if (child.HasValues)
                    {
                        tokenStack.Push(child.Children());
                    }
    
                    if (property.Value.Type == JTokenType.String)
                    {
                        property.Value = property.Value.ToString().ToLowerInvariant();
                    }
                }
                else if (child.Type == JTokenType.Array && child.HasValues)
                {
                    JArray array = (JArray)child;
                    JToken[] arrayItems = new JToken[array.Count];
                    int idx = 0;
                    bool modified = false;
    
                    foreach (JToken arrayItem in array.Children())
                    {
                        arrayItems[idx++] = arrayItem;
                    }
    
                    for (int i = 0; i < arrayItems.Length; ++i)
                    {
                        JToken token = arrayItems[i];
    
                        if (token.Type == JTokenType.String)
                        {
                            modified = true;
                            arrayItems[i] = token.ToString().ToLowerInvariant();
                        }
                    }
    
                    if (modified)
                    {
                        array.Clear();
    
                        foreach (JToken item in arrayItems)
                        {
                            array.Add(item);
                        }
                    }
                }
                else if (child.HasValues)
                {
                    tokenStack.Push(child.Children());
                }
            }
        }
    
        return startToken;
    }
    
Peter Friend
  • 750
  • 1
  • 7
  • 17
  • Do you mean, field names or field values? – dbc Aug 25 '17 at 16:50
  • https://stackoverflow.com/questions/2789593/force-lowercase-property-names-from-json-in-asp-net-mvc – vdefeo Aug 25 '17 at 16:51
  • Values. I edited the question. – Peter Friend Aug 25 '17 at 17:08
  • Since it's the values that you want forced to lowercase I don't think this is a job for JSON.net or any Json serialiser. Can't you simply pass all of the values through a conversion function before saving to CosmosDB? Or if you know they're all strings can't you just say someDynamicObject.someProperty.ToString().ToLower() on each of the fields? – Dan Aug 25 '17 at 17:34

1 Answers1

0

For a similar question, I found the use of JToken very powerful, as this example (SO) shows how to iterate through a JSON data structure and manipulate each data node based on type or content.

devio
  • 36,858
  • 7
  • 80
  • 143
  • 1
    I think this would work if I had control over serialization. I edited my question to note that I am using Azure CosmosDB, and their client library handles serialization. I can pass in a serializer, but not the serialized string. – Peter Friend Aug 25 '17 at 17:31