Json.net rename properties - c#

I have a string representing JSON and I want to rename some of the properties using JSON.NET. I need a generic function to use for any JSON. Something like:
public static void Rename(JContainer container, Dictiontionary<string, string> mapping)
{
foreach (JToken el in container.Children())
{
JProperty p = el as JProperty;
if(el != null && mapping.ContainsKey(p.Name))
{
// **RENAME THIS NODE!!**
}
// recursively rename nodes
JContainer pcont = el as JContainer;
if(pcont != null)
{
Rename(pcont, mapping);
}
}
}
How to do it??

I would suggest reconstructing your JSON with renamed properties. I don't think you should worry about speed penalties as it's usually not an issue. Here's how you can do it.
public static JToken Rename(JToken json, Dictionary<string, string> map)
{
return Rename(json, name => map.ContainsKey(name) ? map[name] : name);
}
public static JToken Rename(JToken json, Func<string, string> map)
{
JProperty prop = json as JProperty;
if (prop != null)
{
return new JProperty(map(prop.Name), Rename(prop.Value, map));
}
JArray arr = json as JArray;
if (arr != null)
{
var cont = arr.Select(el => Rename(el, map));
return new JArray(cont);
}
JObject o = json as JObject;
if (o != null)
{
var cont = o.Properties().Select(el => Rename(el, map));
return new JObject(cont);
}
return json;
}
And here's an example of usage:
var s = #"{ ""A"": { ""B"": 1, ""Test"": ""123"", ""C"": { ""Test"": [ ""1"", ""2"", ""3"" ] } } }";
var json = JObject.Parse(s);
var renamed = Rename(json, name => name == "Test" ? "TestRenamed" : name);
renamed.ToString().Dump(); // LINQPad output
var dict = new Dictionary<string, string> { { "Test", "TestRenamed"} };
var renamedDict = Rename(json, dict);
renamedDict.ToString().Dump(); // LINQPad output

We use this approach. You can find the property you want using JObject's SelectToken(). Yes it does support JsonPath.
public static class NewtonsoftExtensions
{
public static void Rename(this JToken token, string newName)
{
var parent = token.Parent;
if (parent == null)
throw new InvalidOperationException("The parent is missing.");
var newToken = new JProperty(newName, token);
parent.Replace(newToken);
}
}

Related

How do you read a simple value out of some json using System.Text.Json?

I have this json
{"id":"48e86841-f62c-42c9-ae20-b54ba8c35d6d"}
How do I get the 48e86841-f62c-42c9-ae20-b54ba8c35d6d out of it? All examples I can find show to do something like
var o = System.Text.Json.JsonSerializer.Deserialize<some-type>(json);
o.id // <- here's the ID!
But I don't have a type that fits this definition and I don't want to create one. I've tried deserializing to dynamic but I was unable to get that working.
var result = System.Text.Json.JsonSerializer.Deserialize<dynamic>(json);
result.id // <-- An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Linq.Expressions.dll but was not handled in user code: ''System.Text.Json.JsonElement' does not contain a definition for 'id''
Can anyone give any suggestions?
edit:
I just figured out I can do this:
Guid id = System.Text.Json.JsonDocument.Parse(json).RootElement.GetProperty("id").GetGuid();
This does work - but is there a better way?
you can deserialize to a Dictionary:
var dict = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(json)
Or just deserialize to Object which will yield a JsonElement that you can call GetProperty on.
Support for JsonObject has been added in .NET 6 using System.Text.Json.Nodes.
Example:
const string Json = "{\"MyNumber\":42, \"MyArray\":[10,11]}";
// dynamic
{
dynamic obj = JsonNode.Parse(Json);
int number = (int)obj["MyNumber"];
Debug.Assert(number == 42);
obj["MyString"] = "Hello";
Debug.Assert((string)obj["MyString"] == "Hello");
}
// JsonObject
{
JsonObject obj = JsonNode.Parse(Json).AsObject();
int number = (int)obj["MyNumber"];
Debug.Assert(number == 42);
obj["MyString"] = "Hello";
Debug.Assert((string)obj["MyString"] == "Hello");
}
Sources:
https://github.com/dotnet/runtime/issues/53195
https://github.com/dotnet/runtime/issues/45188
I've recently migrated a project from ASP.NET Core 2.2 to 3, and I'm having this inconvenience. In our team we value lean dependencies, so we are trying to avoid including Newtonsoft.JSON back and try using System.Text.Json. We also decided not to use a ton of POCO objects just for JSON serialization, because our backend models are more complex than needed for Web APIs. Also, because of nontrivial behaviour encapsulation, the backend models cannot be easily used to serialize/deserialize JSON strings.
I understand that System.Text.Json is supposed to be faster than Newtonsoft.JSON, but I believe this has a lot to do with ser/deser from/to specific POCO classes. Anyway, speed was not on our list of pros/cons for this decision, so YMMV.
Long story short, for the time being I wrote a small dynamic object wrapper that unpacks the JsonElements from System.Text.Json and tries to convert/cast as best as possible. The typical usage is to read the request body as a dynamic object. Again, I'm pretty sure this approach kills any speed gains, but that was not a concern for our use case.
This is the class:
public class ReflectionDynamicObject : DynamicObject {
public JsonElement RealObject { get; set; }
public override bool TryGetMember (GetMemberBinder binder, out object result) {
// Get the property value
var srcData = RealObject.GetProperty (binder.Name);
result = null;
switch (srcData.ValueKind) {
case JsonValueKind.Null:
result = null;
break;
case JsonValueKind.Number:
result = srcData.GetDouble ();
break;
case JsonValueKind.False:
result = false;
break;
case JsonValueKind.True:
result = true;
break;
case JsonValueKind.Undefined:
result = null;
break;
case JsonValueKind.String:
result = srcData.GetString ();
break;
case JsonValueKind.Object:
result = new ReflectionDynamicObject {
RealObject = srcData
};
break;
case JsonValueKind.Array:
result = srcData.EnumerateArray ()
.Select (o => new ReflectionDynamicObject { RealObject = o })
.ToArray ();
break;
}
// Always return true; other exceptions may have already been thrown if needed
return true;
}
}
and this is an example usage, to parse the request body - one part is in a base class for all my WebAPI controllers, that exposes the body as a dynamic object:
[ApiController]
public class WebControllerBase : Controller {
// Other stuff - omitted
protected async Task<dynamic> JsonBody () {
var result = await JsonDocument.ParseAsync (Request.Body);
return new ReflectionDynamicObject {
RealObject = result.RootElement
};
}
}
and can be used in the actual controller like this:
//[...]
[HttpPost ("")]
public async Task<ActionResult> Post () {
var body = await JsonBody ();
var name = (string) body.Name;
//[...]
}
//[...]
If needed, you can integrate parsing for GUIDs or other specific data types as needed - while we all wait for some official / framework-sanctioned solution.
Actual way to parse string in System.Text.Json (.NET Core 3+)
var jsonStr = "{\"id\":\"48e86841-f62c-42c9-ae20-b54ba8c35d6d\"}";
using var doc = JsonDocument.Parse(jsonStr);
var root = doc.RootElement;
var id = root.GetProperty("id").GetGuid();
You can use the following extension method to query data like "xpath"
public static string? JsonQueryXPath(this string value, string xpath, JsonSerializerOptions? options = null) => value.Deserialize<JsonElement>(options).GetJsonElement(xpath).GetJsonElementValue();
public static JsonElement GetJsonElement(this JsonElement jsonElement, string xpath)
{
if (jsonElement.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
return default;
string[] segments = xpath.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
foreach (var segment in segments)
{
if (int.TryParse(segment, out var index) && jsonElement.ValueKind == JsonValueKind.Array)
{
jsonElement = jsonElement.EnumerateArray().ElementAtOrDefault(index);
if (jsonElement.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
return default;
continue;
}
jsonElement = jsonElement.TryGetProperty(segment, out var value) ? value : default;
if (jsonElement.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
return default;
}
return jsonElement;
}
public static string? GetJsonElementValue(this JsonElement jsonElement) => jsonElement.ValueKind != JsonValueKind.Null &&
jsonElement.ValueKind != JsonValueKind.Undefined
? jsonElement.ToString()
: default;
Simple to use as follows
string raw = #"{
""data"": {
""products"": {
""edges"": [
{
""node"": {
""id"": ""gid://shopify/Product/4534543543316"",
""featuredImage"": {
""originalSrc"": ""https://cdn.shopify.com/s/files/1/0286/pic.jpg"",
""id"": ""gid://shopify/ProductImage/146345345339732""
}
}
},
{
""node"": {
""id"": ""gid://shopify/Product/123456789"",
""featuredImage"": {
""originalSrc"": ""https://cdn.shopify.com/s/files/1/0286/pic.jpg"",
""id"": [
""gid://shopify/ProductImage/123456789"",
""gid://shopify/ProductImage/666666666""
]
},
""1"": {
""name"": ""Tuanh""
}
}
}
]
}
}
}";
System.Console.WriteLine(raw2.QueryJsonXPath("data.products.edges.0.node.featuredImage.id"));
I wrote an extension method for this purpose. You can safely use as following:
var jsonElement = JsonSerializer.Deserialize<JsonElement>(json);
var guid = jsonElement.TryGetValue<Guid>("id");
This is the extension class.
public static class JsonElementExtensions
{
private static readonly JsonSerializerOptions options = new() { PropertyNameCaseInsensitive = true };
public static T? TryGetValue<T>(this JsonElement element, string propertyName)
{
if (element.ValueKind != JsonValueKind.Object)
{
return default;
}
element.TryGetProperty(propertyName, out JsonElement property);
if (property.ValueKind == JsonValueKind.Undefined ||
property.ValueKind == JsonValueKind.Null)
{
return default;
}
try
{
return property.Deserialize<T>(options);
}
catch (JsonException)
{
return default;
}
}
}
Reason
The reason behind using this extension instead of JsonNode class is because if you need a Controller method accepts just an object without exposing it's model class Asp.Net Core model binding uses JsonElement struct to map the json string. At this point (as far as I know) there is no simple way to convert the JsonElement to JsonNode and when your object can be anything the JsonElement methods will throw exceptions for undefined fields while JsonNode don't.
[HttpPost]
public IActionResult Post(object setupObject)
{
var setup = (JsonElement)setupObject;
var id = setup.TryGetValue<Guid>("id");
var user = setup.TryGetValue<User?>("user");
var account = setup.TryGetValue<Account?>("account");
var payments = setup.TryGetValue<IEnumerable<Payments>?>("payments");
// ...
return Ok();
}
update to .NET Core 3.1 to support
public static dynamic FromJson(this string json, JsonSerializerOptions options = null)
{
if (string.IsNullOrEmpty(json))
return null;
try
{
return JsonSerializer.Deserialize<ExpandoObject>(json, options);
}
catch
{
return null;
}
}
You can also deserialize your json to an object of your target class, and then read its properties as per normal:
var obj = DeSerializeFromStrToObj<ClassToSerialize>(jsonStr);
Console.WriteLine($"Property: {obj.Property}");
where DeSerializeFromStrToObj is a custom class that makes use of reflection to instantiate an object of a targeted class:
public static T DeSerializeFromStrToObj<T>(string json)
{
try
{
var o = (T)Activator.CreateInstance(typeof(T));
try
{
var jsonDict = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
var props = o.GetType().GetProperties();
if (props == null || props.Length == 0)
{
Debug.WriteLine($"Error: properties from target class '{typeof(T)}' could not be read using reflection");
return default;
}
if (jsonDict.Count != props.Length)
{
Debug.WriteLine($"Error: number of json lines ({jsonDict.Count}) should be the same as number of properties ({props.Length})of our class '{typeof(T)}'");
return default;
}
foreach (var prop in props)
{
if (prop == null)
{
Debug.WriteLine($"Error: there was a prop='null' in our target class '{typeof(T)}'");
return default;
}
if (!jsonDict.ContainsKey(prop.Name))
{
Debug.WriteLine($"Error: jsonStr does not refer to target class '{typeof(T)}'");
return default;
}
var value = jsonDict[prop.Name];
Type t = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
object safeValue = value ?? Convert.ChangeType(value, t);
prop.SetValue(o, safeValue, null); // initialize property
}
return o;
}
catch (Exception e2)
{
Debug.WriteLine(e2.Message);
return o;
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
return default;
}
}
You can test your jsons for example here
Here you find a complete working example with different ways of serialization and deserialization that might be of interest for you and/or future readers:
using System;
using System.Collections.Generic;
using System.Text.Json;
using static Json_Tests.JsonHelpers;
namespace Json_Tests
{
public class Class1
{
public void Test()
{
var obj1 = new ClassToSerialize();
var jsonStr = obj1.ToString();
// if you have the class structure for the jsonStr (for example, if you have created the jsonStr yourself from your code):
var obj2 = DeSerializeFromStrToObj<ClassToSerialize>(jsonStr);
Console.WriteLine($"{nameof(obj2.Name)}: {obj2.Name}");
// if you do not have the class structure for the jsonStr (forexample, jsonStr comes from a 3rd party service like the web):
var obj3 = JsonSerializer.Deserialize<object>(jsonStr) as JsonElement?;
var propName = nameof(obj1.Name);
var propVal1 = obj3?.GetProperty("Name");// error prone
Console.WriteLine($"{propName}: {propVal1}");
JsonElement propVal2 = default;
obj3?.TryGetProperty("Name", out propVal2);// error prone
Console.WriteLine($"{propName}: {propVal2}");
var obj4 = DeSerializeFromStrToDict(jsonStr);
foreach (var pair in obj4)
Console.WriteLine($"{pair.Key}: {pair.Value}");
}
}
[Serializable]
public class ClassToSerialize
{
// important: properties must have at least getters
public string Name { get; } = "Paul";
public string Surname{ get; set; } = "Efford";
public override string ToString() => JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true });
}
public static class JsonHelpers
{
/// <summary>
/// to use if you do not have the class structure for the jsonStr (forexample, jsonStr comes from a 3rd party service like the web)
/// </summary>
public static Dictionary<string, string> DeSerializeFromStrToDict(string json)
{
try
{
return JsonSerializer.Deserialize<Dictionary<string, string>>(json);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return new Dictionary<string, string>(); // return empty
}
}
/// <summary>
/// to use if you have the class structure for the jsonStr (for example, if you have created the jsonStr yourself from your code)
/// </summary>
public static T DeSerializeFromStrToObj<T>(string json) // see this: https://json2csharp.com/#
{
try
{
var o = (T)Activator.CreateInstance(typeof(T));
try
{
var jsonDict = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
var props = o.GetType().GetProperties();
if (props == null || props.Length == 0)
{
Console.WriteLine($"Error: properties from target class '{typeof(T)}' could not be read using reflection");
return default;
}
if (jsonDict.Count != props.Length)
{
Console.WriteLine($"Error: number of json lines ({jsonDict.Count}) should be the same as number of properties ({props.Length})of our class '{typeof(T)}'");
return default;
}
foreach (var prop in props)
{
if (prop == null)
{
Console.WriteLine($"Error: there was a prop='null' in our target class '{typeof(T)}'");
return default;
}
if (!jsonDict.ContainsKey(prop.Name))
{
Console.WriteLine($"Error: jsonStr does not refer to target class '{typeof(T)}'");
return default;
}
var value = jsonDict[prop.Name];
Type t = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
object safeValue = value ?? Convert.ChangeType(value, t);
prop.SetValue(o, safeValue, null); // initialize property
}
return o;
}
catch (Exception e2)
{
Console.WriteLine(e2.Message);
return o;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return default;
}
}
}
}

Move property child object to root

I have a c# object that natively serializes to
{
"LrsFeature": {
"MEASURE": 1.233242,
"STATION_ID": "brians station",
"NLF_ID": "brians route"
},
"EVENT_ID": "00000000-0000-0000-0000-000000000000",
}
and I want it to be
{
"MEASURE": 1.233242,
"STATION_ID": "brians station",
"NLF_ID": "brians route",
"EVENT_ID": "00000000-0000-0000-0000-000000000000",
}
where all the properties within LrsFeature are added to the root level.
My attempt
var events = JObject.FromObject(LrsEvent);
var attrs = JObject.FromObject(LrsEvent.LrsFeature);
events.Merge(attrs, new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
});
This gives me
{
"MEASURE": 1.233242,
"STATION_ID": "brians station",
"NLF_ID": "brians route",
"LrsFeature": {
"MEASURE": 1.233242,
"STATION_ID": "brians station",
"NLF_ID": "brians route"
},
"EVENT_ID": "00000000-0000-0000-0000-000000000000",
}
I then need to delete the LrsFeature object, but it seems a little bit hacky. I figured JSON.NET might have a more direct method of doing this
You can do what you want like this:
JObject jo = JObject.FromObject(LrsEvent);
JProperty lrs = jo.Property("LrsFeature");
jo.Add(lrs.Value.Children<JProperty>());
lrs.Remove();
string json = jo.ToString();
Fiddle: https://dotnetfiddle.net/zsOQFE
What you're looking to do is deserialize a JSON string and then deep-flatten it. You can do this using the recursive method:
Code
public class JsonExtensions
{
/// <summary>
/// Deeply flattens a json object to a dictionary.
/// </summary>
/// <param name="jsonStr">The json string.</param>
/// <returns>The flattened json in dictionary kvp.</returns>
public static Dictionary<string, object> DeepFlatten(string jsonStr)
{
var dict = new Dictionary<string, object>();
var token = JToken.Parse(jsonStr);
FillDictionaryFromJToken(dict, token, String.Empty);
return dict;
}
private static void FillDictionaryFromJToken(Dictionary<string, object> dict, JToken token, string prefix)
{
if(token.Type == JTokenType.Object)
{
foreach (var property in token.Children<JProperty>())
{
FillDictionaryFromJToken(dict, property.Value, property.Name);
// Uncomment and replace if you'd like prefixed index
// FillDictionaryFromJToken(dict, value, Prefix(prefix, property.Name));
}
}
else if(token.Type == JTokenType.Array)
{
var idx = 0;
foreach (var value in token.Children())
{
FillDictionaryFromJToken(dict, value, String.Empty);
idx++;
// Uncomment and replace if you'd like prefixed index
// FillDictionaryFromJToken(dict, value, Prefix(prefix, idx.ToString()));
}
}
else // The base case
{
dict[prefix] = ((JValue)token).Value; // WARNING: will ignore duplicate keys if you don't use prefixing!!!
}
}
private static string Prefix(string prefix, string tokenName)
{
return String.IsNullOrEmpty(prefix) ? tokenName : $"{prefix}.{tokenName}";
}
}
Usage
var jsonStr = "{\"LrsFeature\":{\"MEASURE\":1.233242,\"STATION_ID\":\"brians station\",\"NLF_ID\":\"brians route\"},\"EVENT_ID\":\"00000000-0000-0000-0000-000000000000\"}";
var dict = JsonExtensions.DeepFlatten(jsonStr);
foreach (var kvp in dict)
Console.WriteLine($"{kvp.Key}={kvp.Value}");
An extension method handling a simple case:
public static JProperty MoveToParent(this JProperty property)
{
if (property is { Parent: JObject { Parent: JProperty { Parent: JObject parent } } })
{
property.Remove();
parent.Add(property);
return property;
}
throw new InvalidOperationException("Could not move to parent.");
}

json.net: how can I deserialize 'similar but different' external json structures to a single class

I'm looking for a way, preferably using JSON.NET (using latest), to deserialize multiple external JSON formats/structures to a single class.
simplified example (the differences are greater than this but the different json's contain similar information):
external 1
{
"id": 1234,
"person": {
"name": "john",
"surname": "doe"
}
}
external 2
{
"ref": "1234",
"firstName": "JOHN",
"lastName": "DOE"
}
internal (this is not real, it's just for show)
{
"tag": "1234",
"name1": "John",
"name2": "Doe"
}
Is there some way/library which perhaps allows you to configure the mapping using a mapping.json file. Preferably one that also allows formatting of the values etc. These are only 2 examples, but we have many more.
Edit:
We can tell/hint JSON.NET what source the given JSON is coming from. Therefor we don't have to have a single schema/contract/solution that can handle all different scenarios. We would actually prefer to have a .json mapping/transform config file for every different external json structure (to keep it future proof without the need of having to rebuild everything).
Edit 2:
What I've now done is the following based on Pavel Baravik answer is to go through all properties of a 'schema/transformation' JSON. This JSON has the same structure as the final JSON of the object that we want to transform the original external JSON to. If a token is of type 'String' we'll parse that string (supports {{ }} and plain =) and use that as a path to pull values from the original external JSON. In the meantime the final JSON is being constructed, which afterwards will be deserialized to our internal object.
I think we could improve the performance of this code by 'sort-of' compiling it using an Expression tree.
static void Main(string[] args)
{
var schema = #"
{
""person"": {
""name"": ""=test.firstName"",
""fullName"": ""{{test.firstName}} {{lastName}}"",
""surName"": ""=lastName""
}
}";
var json1 = #"
{
""test"": {
""firstName"": ""John""
},
""lastName"": ""Doe"",
}";
Console.WriteLine(Transform(json1, schema).ToString());
Console.ReadLine();
}
public static JObject Transform(string json, string schema)
{
var j = JObject.Parse(json);
var s = JObject.Parse(schema);
var t = Transform(s, j);
return t;
}
public static JObject Transform(JObject schema, JObject source)
{
var target = new JObject();
foreach (var child in schema.Children())
{
var property = child as JProperty;
if (property != null)
{
var schemaToken = property.Value;
var allowClone = true;
JToken t = null;
if (schemaToken.Type == JTokenType.Object)
{
t = Transform((JObject) schemaToken, source);
}
else if (schemaToken.Type == JTokenType.String)
{
allowClone = false;
t = TransformProperty(source, (JValue)schemaToken);
}
if (t != null || allowClone)
{
target.Add(property.Name, (t ?? property).DeepClone());
}
}
}
return target;
}
private static readonly Regex MoustacheRegex = new Regex(#"\{\{[^\}]+\}\}", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline);
private static JToken TransformProperty(JObject source, JValue jstring)
{
var str = (string)jstring.Value;
JToken t = null;
// if string starts with =
if (str.StartsWith("="))
{
t = GetTokenByPath(source, str.Substring(1));
}
else
{
var allFound = true;
str = MoustacheRegex.Replace(str, m =>
{
var mv = m.Value;
var mt = GetTokenByPath(source, mv.Substring(2, mv.Length - 4));
if (mt == null) allFound = false;
return mt?.ToString() ?? string.Empty;
});
if (allFound)
t = new JValue(str.Trim());
}
return t;
}
private static JToken GetTokenByPath(JObject source, string path)
{
JToken t = null;
var pathItems = path.Split('.');
var s = source;
for (var i = 0; i < pathItems.Length && s != null; ++i, s = t as JObject)
{
t = s[pathItems[i]];
}
return t;
}
EDIT: (nice JTransform class)
public class JTransform
{
private InternalJTransform _internal;
public void Load(string filePath)
{
using (var stream = File.OpenRead(filePath))
using (var reader = new StreamReader(stream))
{
Load(new JsonTextReader(reader));
}
}
public void Load(string filePath, Encoding encoding)
{
using (var stream = File.OpenRead(filePath))
using (var reader = new StreamReader(stream, encoding))
{
Load(new JsonTextReader(reader));
}
}
public void Load(JsonReader reader)
{
_internal = new InternalJTransform(reader);
}
public JObject Transform(JsonReader sourceReader)
{
return _internal.Transform(sourceReader);
}
public JObject Transform(JObject source)
{
return _internal.Transform(source);
}
public T TransformObject<T>(object obj)
{
return _internal.TransformObject<T>(obj);
}
public T TransformObject<T>(JObject source, JsonSerializer serializer = null)
{
return _internal.TransformObject<T>(source, serializer);
}
#region InternalJTransform
private sealed class InternalJTransform
{
private static readonly Regex MoustacheRegex = new Regex(#"\{\{[^\}]+\}\}", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline);
private JsonSerializer _serializer;
private JObject _template;
private bool _ignoreUndefined;
public InternalJTransform(JsonReader reader)
{
var json = JObject.Load(reader);
_template = json["template"] as JObject;
_serializer = new JsonSerializer();
var settings = json["settings"];
if (settings["camelCase"]?.Value<bool>() ?? false)
_serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
if (settings["ignoreNull"]?.Value<bool>() ?? false)
_serializer.NullValueHandling = NullValueHandling.Ignore;
_ignoreUndefined = (settings["ignoreUndefined"]?.Value<bool>() ?? settings["ignoreNull"]?.Value<bool>() ?? false);
}
private void Load(JsonReader reader)
{
var json = JObject.Load(reader);
var template = json["template"] as JObject;
var serializer = new JsonSerializer();
var settings = json["settings"];
if (settings["camelCase"]?.Value<bool>() ?? false)
serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
if (settings["ignoreNull"]?.Value<bool>() ?? false)
serializer.NullValueHandling = NullValueHandling.Ignore;
_ignoreUndefined = (settings["ignoreNull"]?.Value<bool>() ?? false);
_serializer = serializer;
_template = template;
}
public JObject Transform(JsonReader sourceReader)
{
var obj = JObject.Load(sourceReader);
return TransformInternal(_template, obj, _serializer);
}
public JObject Transform(JObject source)
{
return TransformInternal(_template, source, _serializer);
}
public T TransformObject<T>(object obj)
{
var source = JObject.FromObject(obj);
var im = TransformInternal(_template, source, _serializer);
return im.ToObject<T>(_serializer);
}
public T TransformObject<T>(JObject source, JsonSerializer serializer = null)
{
var obj = TransformInternal(_template, source, _serializer);
return obj.ToObject<T>(serializer ?? _serializer);
}
private JObject TransformInternal(JObject template, JObject source, JsonSerializer serializer)
{
var ignoreNull = serializer.NullValueHandling == NullValueHandling.Ignore;
var target = new JObject();
foreach (var property in template.Properties())
{
var token = property.Value;
if (token.Type == JTokenType.Object)
{
token = TransformInternal((JObject)token, source, serializer);
}
else if (token.Type == JTokenType.String)
{
token = TransformStringToken(source, (JValue)token);
// handle undefined, not found, values
if (token == null && _ignoreUndefined) continue;
}
// handle real null values (this does not include null values set in the template)
if (token != null && token.Type == JTokenType.Null && ignoreNull) continue;
target.Add(property.Name, token?.DeepClone());
}
return target;
}
private JToken TransformStringToken(JObject source, JValue jstring)
{
var str = (string)jstring.Value;
JToken t = null;
// if string starts with =
if (str.StartsWith("="))
{
t = GetTokenByPath(source, str.Substring(1));
}
else
{
var allFound = true;
str = MoustacheRegex.Replace(str, m =>
{
var mv = m.Value;
var mt = GetTokenByPath(source, mv.Substring(2, mv.Length - 4));
if (mt == null) allFound = false;
return mt?.ToString() ?? string.Empty;
});
if (allFound)
t = new JValue(str.Trim());
}
return t;
}
private static JToken GetTokenByPath(JObject source, string path)
{
JToken t = null;
var pathItems = path.Split('.');
var s = source;
for (var i = 0; i < pathItems.Length && s != null; ++i, s = t as JObject)
{
t = s[pathItems[i]];
}
return t;
}
}
#endregion
}
You can firstly 'flatten' your input structures with use of JsonReader and then map to a single class (adopted from JSON.NET deserialize a specific property).
void Main()
{
var json0 = #"{
""id"": 1234,
""person"": {
""name"": ""john"",
""surname"": ""doe""
}";
var json1 = #" {
""ref"": ""1234"",
""firstName"": ""JOHN"",
""lastName"": ""DOE""
}";
foreach (var j in new []{json0, json1})
{
var name = GetFirstInstance<string>(new [] {"person.name", "firstName", "name1"}, j);
var surname = GetFirstInstance<string> (new [] {"person.surname", "lastName", "name2"}, j);
new {name, surname}.Dump();
}
}
public T GetFirstInstance<T>(string[] path, string json)
{
using (var stringReader = new StringReader(json))
using (var jsonReader = new JsonTextReader(stringReader))
{
while (jsonReader.Read())
{
if (jsonReader.TokenType == JsonToken.PropertyName && path.Contains((string)jsonReader.Path))
{
jsonReader.Read();
var serializer = new JsonSerializer();
return serializer.Deserialize<T>(jsonReader);
}
}
return default(T);
}
}
Take a look at the CustomCreationConverter in JSON.NET http://www.newtonsoft.com/json/help/html/CustomCreationConverter.htm you can make different converters and decide which one to use based on the JSON you have. they could all output the same class

Flattening out a JToken

Suppose I have the following JToken:
#"{
""data"": [
{
""company"": {
""ID"": ""12345"",
""location"": ""Some Location""
},
""name"": ""Some Name""
}
]
}";
I want to pass this token into a FlattenToken function that outputs this JToken:
#"{
""data"": [
{
""company_ID"": ""12345"",
""company_location"": ""Some Location"",
""name"": ""Some Name""
}
]}"
The reason for doing this is so that I can then take the flattened JToken and deserialize it into a DataTable.
I'm getting lost in a jumble of JObjects, JTokens, JProperties, and other JMadness, though. I saw the answer on this post, which was helpful, but I'm still not getting it right.
Here's what I have so far:
public static JToken FlattenToken(JToken token)
{
foreach (JToken topLevelItem in token["data"].Children())
{
foreach (JToken field in topLevelItem.Value<JToken>())
{
foreach (JProperty property in field.Value<JObject>().Properties())
{
field.AddAfterSelf(JObject.Parse(#"{""" + property.Name + "_" + property.Value));
}
field.Remove();
}
}
return token;
}
The first iteration through the outer foreach loop, topLevelItem =
{
"company": {
"ID": "12345"
},
"name": "Some Name"
}
And the first iteration through the second foreach loop, field =
"company": {
"ID": "12345"
}
Looking good so far. But when I hit the innermost foreach loop, I get an exception on the foreach line: "Cannot cast Newtonsoft.Json.Linq.JProperty to Newtonsoft.Json.Linq.JToken."
Not sure what's going on there. I was under the impression that the field.Value call was going to produce a JToken and try to cast it to a JProperty. So where is a JProperty trying to be casted to a JToken, as the error suggests?
Also, this feels like a pretty gross way of flattening out a JToken. Is there a better way?
The hierarchy of objects in Json.NET can be rather deep. A rough guide can be found in this answer.
To solve your problem, you first need an extension method to take the properties of a JObject and return then in a collection with a name prefix:
public static class JsonExtensions
{
public static IEnumerable<KeyValuePair<string, JToken>> FlattenFields(this JObject obj, string prefix)
{
foreach (var field in obj)
{
string fieldName = prefix + "_" + field.Key;
var fieldValue = field.Value;
yield return new KeyValuePair<string, JToken>(fieldName, fieldValue);
}
}
}
Next, you need some recursive tools to iterate through a Json.NET hierarchy and rewrite the collection of properties of selected JObject's:
public static class JsonExtensions
{
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
public static JToken EditFields(this JToken token, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor)
{
if (token == null)
return null;
switch (token.Type)
{
case JTokenType.Array:
return EditFields((JArray)token, editor);
case JTokenType.Object:
return EditFields((JObject)token, editor);
default:
return token;
}
}
static JToken EditFields(JArray array, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor)
{
JArray newArray = null;
foreach (var element in array)
{
var newElement = EditFields(element, editor);
if (newElement != null)
{
if (newArray == null)
newArray = new JArray();
newArray.Add(newElement);
}
}
return newArray;
}
static JToken EditFields(JObject obj, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor)
{
JObject newObj = null;
foreach (var field in obj)
{
foreach (var newField in editor(field))
{
if (newObj == null)
newObj = new JObject();
newObj[newField.Key] = newField.Value.EditFields(editor);
}
}
return newObj;
}
}
Finally, put these together to create a method that promotes properties of a named JObject property to their parent JObject, prepending the property name plus an underscore:
public static class JsonExtensions
{
public static JToken PromoteNamedPropertiesToParents(this JToken token, string propertyName)
{
return token.EditFields(pair =>
{
if (pair.Key == propertyName && pair.Value is JObject)
{
return ((JObject)pair.Value).FlattenFields(pair.Key);
}
return pair.Yield();
});
}
}
And then, to test:
public static class TestFlatten
{
public static void Test()
{
string jsonString = #"{
""data"": [
{
""company"": {
""ID"": ""12345"",
""location"": ""Some Location""
},
""name"": ""Some Name""
}
]
}";
JObject obj = JObject.Parse(jsonString);
var newObj = (JObject)obj.PromoteNamedPropertiesToParents("company");
Debug.WriteLine(newObj);
}
}
And the output is:
{
"data": [
{
"company_ID": "12345",
"company_location": "Some Location",
"name": "Some Name"
}
]
}
Which is what you want. Please note that this code creates a new JObject hierarchy rather than modifying the original hierarchy.

Construct JSON payload

I have the following table:
I'd like to execute a LINQ Query that serializes:
JSON:
{
"product-list": {
"products": [
{
"P_Flavor": [
"Berry",
"Cedar",
"Cherry",
"Coffee"
],
"P_Winery": [
"Lyeth"
],
"P_Body": [
"Elegant",
"Firm",
"Firm Tannins",
"Polished",
"Supple",
"Tannins"
],
"P_Name": "A Red Blend",
"P_DateReviewed": "08/31/95",
"P_WineID": 34699,
"P_Score": 5,
}
]
}
}
I would normally use JavaScriptSerializer to do this, however I would like to construct my own JSON payload.
IList<record_property> recList = (from c in entities.record_property
select c).ToList();
var json = new JavaScriptSerializer().Serialize(recList);
What would be the best way to do this?
There may be a quicker/more concise way to do this, but I did it by combining a JavaScriptConverter with a helper type.
The converter (simpler than it looks, inspired from here):
private class RecordPropertyJavaScriptConverter : JavaScriptConverter
{
private static readonly Type[] _supportedTypes = new[]
{
typeof(record_group)
};
public override IEnumerable<Type> SupportedTypes
{
get { return _supportedTypes; }
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (type == typeof(record_group))
{
record_group obj = new record_group();
var kvp = dictionary.Single();
obj.Key = kvp.Key;
obj.Values = serializer.ConvertToType<IEnumerable<object>>(kvp.Value);
return obj;
}
return null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var dataObj = obj as record_group;
if (dataObj != null)
{
return new Dictionary<string, object>
{
{dataObj.Key, dataObj.Values}
};
}
return new Dictionary<string, object>();
}
}
The helper type:
private class record_group
{
public string Key;
public IEnumerable<object> Values;
}
The serialization code:
var groups = recList.GroupBy(r => r.Key)
.Select(g => new record_group { Key = g.Key, Values = g.Select(r => r.Value) });
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new [] {new RecordPropertyJavaScriptConverter()});
string json = serializer.Serialize(groups);
The output (with some tabs, newlines added by me):
[{"P_Flavor":["Berry","Cedar","Cherry","Coffee"]},
{"P_Winery":["Lyeth"]},
{"P_Body":["Elegant","Firm","Firm Tannins","Polished","Supple","Tannins"]},
{"P_Name":["A Red Blend"]},
{"P_DateReviewed":["08/31/95"]},
{"P_WineID":[34699]},
{"P_Score":[5]}]
Deserialization can then be done (using the same serializer instance from above) as follows:
var deserialized = serializer.Deserialize<IEnumerable<record_group>>(json);
var properties = deserialized.SelectMany(g => g.Values.Select(v => new record_property { Key = g.Key, Value = v }));

Categories