rtrujillor
rtrujillor

Reputation: 1182

Getting all the properties with a name from a JSON and changing the value

I'm trying to translate the content of a JSON document. This means that I have to translate the values of some properties, not all.

Isolating the problem, I'm trying to get all the properties with a specific name ( e.g title ), that can be at different json levels, then get the values of those properties, translate the value using a translation service, and replace back the value of each one with the translated value.

How can I achieve this using C#?. I'm trying to use Newtonsoft.Json library, and it's a bit confusing for me the usage of JToken, JProperty..

This is the example of the json file:

[
    {
        "_id": "a-10",
        "_parentId": "co-10",
        "_type": "article",
        "_classes": "",
        "title": "THINK INTEGRITY AT X",
        "displayTitle": "SECTION 01: <em>THINK INTEGRITY AT X </em>",
        "body": "<p>ASKING THE RIGHT QUESTIONS.</p>"
    },
    {
        "_id": "a-20",
        "_parentId": "co-20",
        "_type": "article",
        "_classes": "",
        "title": "INTEGRITY OF SERVICES",
        "displayTitle": "SECTION 02: &lt;em&gt;INTEGRITY OF SERVICES&lt;/em&gt;",
        "body": "<p>STAYING INDEPENDENT IN ALL CIRCUMSTANCES.</p>"
    },
    {
        "_id": "a-30",
        "_parentId": "co-30",
        "_type": "article",
        "_classes": "",
        "title": "BRIBERY AND CORRUPTION",
        "displayTitle": "SECTION 03: &lt;em&gt;BRIBERY AND CORRUPTION&lt;/em&gt;",
        "body": "<p>KNOWING WHAT YOU SHOULD AND SHOULDN'T ACCEPT.</p>"
    },
    {
        "_id": "a-40",
        "_parentId": "co-40",
        "_type": "article",
        "_classes": "",
        "title": "CONFLICTS OF INTEREST",
        "displayTitle": "SECTION 04: &lt;em&gt;CONFLICTS OF INTEREST&lt;/em&gt;",
        "body": "<p>RECOGNISING CONFLICTS AND KNOWING WHAT TO DO.</p>"
    },
    {
        "_id": "a-50",
        "_parentId": "co-50",
        "_type": "article",
        "_classes": "",
        "title": "OPERATIONAL INTEGRITY",
        "displayTitle": "SECTION 05: &lt;em&gt;OPERATIONAL INTEGRITY&lt;/em&gt;",
        "body": "<p>LEARN IT. OWN IT. BE IT.</p>"
    },
    {
        "_id": "a-60",
        "_parentId": "co-60",
        "_type": "article",
        "_classes": "",
        "title": "TEST YOURSELF",
        "displayTitle": "SECTION 06: &lt;em&gt;TEST YOURSELF&lt;/em&gt;",
        "body": "<p>PUTTING YOUR INTEGRITY TO THE TEST.</p>"
    }
]

So to translate the content of the "title" property in all the json, can be like this:

string jsonText = File.ReadAllText(jsonfilename);
JArray jsonDocument = JArray.Parse(jsonText);

IEnumerable<JToken> titles = jsonDocument.SelectTokens("..title");

                foreach (var title in titles)
                {
                    string translatedText = TranslationService.TranslateString(title.ToString(), fromlanguage, tolanguage, "text/plain");

                    title.Replace(JToken.FromObject(new { title = translatedText }));

                }

but this is not working, I think I'm close to it but I can't see the way to replace the property value with the translated one.

Remember that the title can be at any level in the JSON file, so to access using the jsonDocument["title"] way is not feasible, and I want to replace the existing value with the translated.

I'll appreciate any clue Thanks

Upvotes: 1

Views: 2983

Answers (2)

Chris
Chris

Reputation: 3303

I modified your example JSON to include "title" at different levels:

{
    "title": "THINK INTEGRITY AT X",
    "list": [{
            "_id": "a-10",
            "_parentId": "co-10",
            "_type": "article",
            "_classes": "",
            "title": "THINK INTEGRITY AT X",
            "displayTitle": "SECTION 01: &lt;em&gt;THINK INTEGRITY AT X &lt;/em&gt;",
            "body": "<p>ASKING THE RIGHT QUESTIONS.</p>"
        }, {
            "_id": "a-20",
            "_parentId": "co-20",
            "_type": "article",
            "_classes": "",
            "obj": {
                "title": "THINK INTEGRITY AT X"
            },
            "displayTitle": "SECTION 02: &lt;em&gt;INTEGRITY OF SERVICES&lt;/em&gt;",
            "body": "<p>STAYING INDEPENDENT IN ALL CIRCUMSTANCES.</p>"
        },
        {
            "_id": "a-20",
            "_parentId": "co-20",
            "_type": "article",
            "_classes": "",
            "obj": {
                "obj2": {
                    "title": "THINK INTEGRITY AT X"
                }
            },
            "displayTitle": "SECTION 02: &lt;em&gt;INTEGRITY OF SERVICES&lt;/em&gt;",
            "body": "<p>STAYING INDEPENDENT IN ALL CIRCUMSTANCES.</p>"
        }
    ]
}

Then using this code (found here Deep find or search the key at any level in JSON and replace its value in c#):

static void Main(string[] args)
{
    string jsonText = File.ReadAllText(jsonfilename);

    var jToken = JToken.Parse(jsonText);

    var replaced = FindAndReplace(jToken, "title", "replacewiththis");

    var final = replaced.ToString(Formatting.Indented);

    Console.ReadLine();
}

public static JToken FindAndReplace(JToken jToken, string key, JToken value, int? occurence = null)
{
    var searchedTokens = jToken.FindTokens(key);
    int count = searchedTokens.Count;

    if (count == 0)
        return $"The key you have to search is not present in json, Key: {key}";

    foreach (JToken token in searchedTokens)
    {
        if (!occurence.HasValue)
            jToken.SetByPath(token.Path, value);
        else
        if (occurence.Value == searchedTokens.IndexOf(token))
            jToken.SetByPath(token.Path, value);
    }

    return jToken;
}

public static class JsonExtensions
{
    public static void SetByPath(this JToken obj, string path, JToken value)
    {
        JToken token = obj.SelectToken(path);
        token.Replace(value);
    }

    public static List<JToken> FindTokens(this JToken containerToken, string name)
    {
        List<JToken> matches = new List<JToken>();
        FindTokens(containerToken, name, matches);
        return matches;
    }

    private static void FindTokens(JToken containerToken, string name, List<JToken> matches)
    {
        if (containerToken.Type == JTokenType.Object)
        {
            foreach (JProperty child in containerToken.Children<JProperty>())
            {
                if (child.Name == name)
                {
                    matches.Add(child.Value);
                }
                FindTokens(child.Value, name, matches);
            }
        }
        else if (containerToken.Type == JTokenType.Array)
        {
            foreach (JToken child in containerToken.Children())
            {
                FindTokens(child, name, matches);
            }
        }
    }
}

produces this result:

{
    "title": "replacewiththis",
    "list": [{
            "_id": "a-10",
            "_parentId": "co-10",
            "_type": "article",
            "_classes": "",
            "title": "replacewiththis",
            "displayTitle": "SECTION 01: &lt;em&gt;THINK INTEGRITY AT X &lt;/em&gt;",
            "body": "<p>ASKING THE RIGHT QUESTIONS.</p>"
        }, {
            "_id": "a-20",
            "_parentId": "co-20",
            "_type": "article",
            "_classes": "",
            "obj": {
                "title": "replacewiththis"
            },
            "displayTitle": "SECTION 02: &lt;em&gt;INTEGRITY OF SERVICES&lt;/em&gt;",
            "body": "<p>STAYING INDEPENDENT IN ALL CIRCUMSTANCES.</p>"
        }, {
            "_id": "a-20",
            "_parentId": "co-20",
            "_type": "article",
            "_classes": "",
            "obj": {
                "obj2": {
                    "title": "replacewiththis"
                }
            },
            "displayTitle": "SECTION 02: &lt;em&gt;INTEGRITY OF SERVICES&lt;/em&gt;",
            "body": "<p>STAYING INDEPENDENT IN ALL CIRCUMSTANCES.</p>"
        }
    ]
}

Upvotes: 4

Miiite
Miiite

Reputation: 1557

To avoid creating a class and serialization you could do something like this :

JArray jsonDocument = JArray.Parse(json);

foreach(var token in jsonDocument)
{
    var obj = token.Value<JObject>();

    obj["title"] = TranslationService.TranslateString(title.ToString(), fromlanguage, tolanguage, "text/plain");
}

Then you need to save your jsonDocument.

Upvotes: 1

Related Questions