You want to rename all properties named "name"
to "profileName"
when nested inside some object ..profiles[*]
in free-form JSON whose schema is not known in advance and that may be very large.
This can be accomplished by installing json.net.
Firstly, if you can load the entire JSON into memory, this can be done very easily using LINQ to JSON which has functionality to parse, modify, and re-serialize arbitrary JSON with any schema. First, grab the extension method public static void Rename(this JToken token, string newName)
from this answer to Rename JProperty in json.net by Brian Rogers. Now you can rename your properties as follows:
var root = JToken.Parse(jsonString);
foreach (var item in root.SelectTokens("..profiles[*].name").ToList())
item.Rename("profileName"); // Using the extension method here
var newJsonString = root.ToString();
Note the use of the method SelectTokens()
. This method provides support for JSONPath - XPath for JSON query syntax to select nested JSON tokens out of the JSON hierarchy matching some pattern. If your actual JSON differs from the JSON in the question you would need to tweak the JSONPath accordingly.
Demo fiddle #1 here.
Secondly, if you cannot load the entire JSON file into memory because of its size, you can implement a streaming transformation from file to file by subclassing JsonTextWriter
and overriding WritePropertyName()
.
Define the following class and extension methods:
public class NameRemappingJsonWriter : JsonTextWriter
{
readonly Func<string, int, string, string> map;
public NameRemappingJsonWriter(TextWriter textWriter, Func<string, int, string, string> map) : base(textWriter)
{
if (map == null)
throw new ArgumentNullException(nameof(map));
this.map = map;
}
public override void WritePropertyName(string name)
{
base.WritePropertyName(map(name, Top, Path));
}
public override void WritePropertyName(string name, bool escape)
{
base.WritePropertyName(map(name, Top, Path), escape);
}
}
public static partial class JsonExtensions
{
public static void StreamRenameJsonProperties(string inFilePath, string outFilePath, string oldName, Regex parentPathRegex, string newName, Formatting formatting = Formatting.Indented)
{
Func<string, int, string, string> map = (name, depth, parentPath) =>
{
if (name == oldName && parentPathRegex.IsMatch(parentPath))
return newName;
return name;
};
StreamRenameJsonProperties(inFilePath, outFilePath, map, formatting);
}
public static void StreamRenameJsonProperties(string inFilePath, string outFilePath, Func<string, int, string, string> map, Formatting formatting = Formatting.Indented)
{
using (var textReader = new StreamReader(inFilePath))
using (var jsonReader = new JsonTextReader(textReader) { DateParseHandling = DateParseHandling.None })
{
using (var textWriter = new StreamWriter(outFilePath))
using (var jsonWriter = new NameRemappingJsonWriter(textWriter, map) { Formatting = formatting })
{
jsonWriter.WriteToken(jsonReader);
}
}
}
}
And you will be able to transform your JSON file as follows:
JsonExtensions.StreamRenameJsonProperties(inFilePath, outFilePath, "name", new Regex("profiles\\[[0-9]+\\]$"), "profileName");
If your actual JSON differs from the JSON in the question you would need to tweak the oldName
and Regex
accordingly.
Demo fiddle #2 here. In both fiddles the output is
[
{
"name": "name 1",
"profiles": [
{
"profileName": "profile 1.1"
},
{
"profileName": "profile 1.2"
}
]
},
{
"name": "name 2",
"profiles": [
{
"profileName": "profile 2.1"
}
]
}
]