I have 2 methods
public static string SerializeObject<T>(T value)
{
if (value == null)
{
return null;
}
var dictionaryObject = new Dictionary<string, object> { { typeof(T).Name, value } };
var jsonString = JsonConvert.SerializeObject(dictionaryObject);
return jsonString;
}
and
public static T DeserializeObject<T>(string jsonString)
{
var objectValue = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonString);
return JsonConvert.DeserializeObject<T>(objectValue.Values.First().ToString());
}
When I deserialize a json string with type
ConcurrentDictionary<KeyValuePair<long, long>, IList<string>>
I have an exception:
Could not convert string '[1, 1]' to dictionary key type 'System.Collections.Generic.KeyValuePair`2[System.Int64,System.Int64]'. Create a TypeConverter to convert from the string to the key type object. Path '[1, 1]', line 2, position 12.
So Can someone show me the right code for it to work?
Here is my code:
var test = new ConcurrentDictionary<KeyValuePair<long, long>, IList<string>>();
test.TryAdd(new KeyValuePair<long, long>(1, 1), new List<string> { "Test" });
var se = SerializeObject(test);
var de = DeserializeObject<ConcurrentDictionary<KeyValuePair<long, long>, IList<string>>>(se);
I'm not sure if this is the best solution, but, please, try this:
1) create ContractResolver as described in this topic.
class DictionaryAsArrayResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (objectType.GetInterfaces().Any(i => i == typeof(IDictionary) ||
(i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IDictionary<,>))))
{
return base.CreateArrayContract(objectType);
}
return base.CreateContract(objectType);
}
}
2) Change a bit your Serialize/Deserialize methods:
public static string SerializeObject<T>(T value, JsonSerializerSettings settings)
{
if (value == null)
{
return null;
}
var dictionaryObject = new Dictionary<string, object> { { typeof(T).Name, value } };
var jsonString = JsonConvert.SerializeObject(dictionaryObject, settings);
return jsonString;
}
public static T DeserializeObject<T>(string jsonString, JsonSerializerSettings settings)
{
var objectValue = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonString, settings);
return JsonConvert.DeserializeObject<T>(objectValue.Values.First().ToString(), settings);
}
3) Check the test:
[TestMethod]
public void Test()
{
var test = new ConcurrentDictionary<KeyValuePair<long, long>, IList<string>>();
test.TryAdd(new KeyValuePair<long, long>(1, 1), new List<string> { "Test" });
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new DictionaryAsArrayResolver();
var se = SerializeObject(test, settings);
var de = DeserializeObject<ConcurrentDictionary<KeyValuePair<long, long>, IList<string>>>(se, settings);
}
I hope, it helps =)
Related
I have a scenario where I will receive a dictionary<string, object>();
that can also be recursive though not always, but the main issue is that it contains lambdas.
as we implement a transaction system I need to clone it, which works fine until I hit the lambdas.
I tried to move these to delegates but the error changes and still gives me runtime issue.
technically I can re inject the lambdas after the cloning, but don't know how to tell the DataContractSerializer to ignore these.
I also cannot remove the lambdas prior to the cloning as the original object still needs them if I cancel the transaction.
In your Clone() method you can use the data contract surrogate functionality to replace all System.Delegate objects in your serialization graph with a serializable type, such as a lookup in a dictionary of delegates that you build as you serialize. Then, as you deserialize, you could replace the deserialized serialization surrogates with the original delegates.
The following does this:
public static class DataContractExtensions
{
public static Dictionary<string, object> Clone(this Dictionary<string, object> dictionary)
{
if (dictionary == null)
return null;
var surrogate = new DelegateSurrogateSelector();
var types = new[]
{
typeof(Dictionary<string, object>),
typeof(DelegateSurrogateId),
// Add in whatever additional known types you want.
};
var serializer = new DataContractSerializer(
typeof(Dictionary<string, object>),
types, int.MaxValue, false, false,
surrogate);
var xml = dictionary.ToContractXml(serializer, null);
return FromContractXml<Dictionary<string, object>>(xml, serializer);
}
public static string ToContractXml<T>(this T obj, DataContractSerializer serializer, XmlWriterSettings settings)
{
serializer = serializer ?? new DataContractSerializer(obj == null ? typeof(T) : obj.GetType());
using (var textWriter = new StringWriter())
{
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
{
serializer.WriteObject(xmlWriter, obj);
}
return textWriter.ToString();
}
}
public static T FromContractXml<T>(string xml, DataContractSerializer serializer)
{
using (var textReader = new StringReader(xml ?? ""))
using (var xmlReader = XmlReader.Create(textReader))
{
return (T)(serializer ?? new DataContractSerializer(typeof(T))).ReadObject(xmlReader);
}
}
[DataContract]
class DelegateSurrogateId
{
[DataMember]
public int Id { get; set; }
}
class DelegateSurrogateSelector : IDataContractSurrogate
{
public Dictionary<int, System.Delegate> DelegateDictionary { get; private set; }
public DelegateSurrogateSelector()
{
this.DelegateDictionary = new Dictionary<int, Delegate>();
}
#region IDataContractSurrogate Members
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public Type GetDataContractType(Type type)
{
if (typeof(Delegate).IsAssignableFrom(type))
return typeof(DelegateSurrogateId);
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
var id = obj as DelegateSurrogateId;
if (id != null)
return DelegateDictionary[id.Id];
return obj;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public object GetObjectToSerialize(object obj, Type targetType)
{
var del = obj as Delegate;
if (del != null)
{
var id = DelegateDictionary.Count;
DelegateDictionary.Add(id, del);
return new DelegateSurrogateId { Id = id };
}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
#endregion
}
}
Using the above Clone() extension method, the following test will pass:
Func<bool> returnTrue = () => true;
Func<bool> returnFalse = () => false;
var dictionary = new Dictionary<string, object>()
{
{ "a", "hello"},
{ "b", 10101 },
{ "c", returnTrue },
{ "d", new Dictionary<string, object>()
{
{ "q", 101 },
{ "r", returnFalse },
}
}
};
var dictionary2 = dictionary.Clone();
Assert.AreEqual(returnTrue, dictionary2["c"]); // No failure
var inner = (Dictionary<string, object>)dictionary["d"];
var inner2 = (Dictionary<string, object>)dictionary2["d"];
Assert.AreEqual(inner["r"], inner2["r"]); // No failure
Notes:
Data contract serialization surrogates work differently in .NET Core. There, the IDataContractSurrogate interface has been replaced with ISerializationSurrogateProvider.
This question already has answers here:
Deserializing polymorphic json classes without type information using json.net
(6 answers)
Closed 5 years ago.
So i'm coding a tcp/ip chat, and i wanted to use json.net to serialize the messages so i can send it through a network.
I got these two types of message:
class AlertMessage
{
public string Client { get; set; }
public ServerManager.Status Status { get; set; } //enum {Connect, Disconnect}
}
class TextMessage
{
public string Client { get; set; }
public string Message { get; set; }
}
to deserialize i need to use the method JsonConvert.DeserializeObject<T>(object), but on the client side i cant realy tell what type arrives, any help is welcome! thanks
If you have no idea about the json you are going to receive, you could use the
using System.Web.Script.Serialization;
namespace. (Easily search and add this reference)
I used the following code to convert any object into a valid json object.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
namespace MinimalMonitoringClient.Helper
{
public sealed class DynamicJsonConverter : JavaScriptConverter
{
public static object ConvertToObject(string data)
{
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
return serializer.Deserialize(data, typeof(object));
}
private static string CleanJson(string data)
{
//Remove leading and ending whitespaces.
data = data.Trim();
//Java Script notation fixes
if(data.StartsWith("["))
data = data.Remove(0, 1);
if(data.EndsWith("]"))
data = data.Remove(data.Length - 1, 1);
return data.Contains("={") ? data.Replace("={", ":{") : data;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
}
#region Nested type: DynamicJsonObject
private sealed class DynamicJsonObject : DynamicObject
{
private readonly IDictionary<string, object> _dictionary;
public DynamicJsonObject(IDictionary<string, object> dictionary)
{
_dictionary = dictionary ?? throw new ArgumentNullException("dictionary");
}
public override string ToString()
{
var sb = new StringBuilder("{");
ToString(sb);
return sb.ToString();
}
private void ToString(StringBuilder sb)
{
var firstInDictionary = true;
foreach (var pair in _dictionary)
{
if (!firstInDictionary)
sb.Append(",");
firstInDictionary = false;
var value = pair.Value;
var name = pair.Key;
if (value is string)
{
sb.AppendFormat("{0}:\"{1}\"", name, value);
}
else if (value is IDictionary<string, object>)
{
new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
}
else if (value is ArrayList)
{
sb.Append(name + ":[");
var firstInArray = true;
foreach (var arrayValue in (ArrayList)value)
{
if (!firstInArray)
sb.Append(",");
firstInArray = false;
if (arrayValue is IDictionary<string, object>)
new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
else if (arrayValue is string)
sb.AppendFormat("\"{0}\"", arrayValue);
else
sb.AppendFormat("{0}", arrayValue);
}
sb.Append("]");
}
else
{
sb.AppendFormat("{0}:{1}", name, value);
}
}
sb.Append("}");
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!_dictionary.TryGetValue(binder.Name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && indexes[0] != null)
{
if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
return base.TryGetIndex(binder, indexes, out result);
}
private static object WrapResultObject(object result)
{
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
return new DynamicJsonObject(dictionary);
var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
return arrayList[0] is IDictionary<string, object>
? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)))
: new List<object>(arrayList.Cast<object>());
}
return result;
}
}
#endregion
}
}
Now simply convert any json into an object with
object jsonObject = DynamicJsonConverter.ConvertToObject(Json);
Hope that helps
Not sure if I have understood your question correctly, but you can Deserialize object it as dynamic. For example:
var converter = new ExpandoObjectConverter();
dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(json, converter);
My objective:
To create a list of objects with dynamically generated properties which can be passed to WCF service parameter as a JSON array.
Final outcome is the following:
[{id:2, name:"John", country:"Germany"},...] or
[{id:3, city:"Sydney"},...]
From WCF I don't have the convenience of working with a class, i.e. "Thing" so I could do this:
public List<Thing> MyThings {get; set;}
I do not know what the properties are at run time. I have experimented with System.Dynamic namespace using
List<ExpandoObject>
Unfortunately, dynamic code will give me the error message:
Dynamic operations can only be performed in homogenous AppDomain.
To allow dynamic code to run I would have to change legacyCasModel tag in the web.config. Unfortunately I cannot change the web.config.
Another option is to use Dictionary array but I do not want the key/value pair format:
[{key:id}, {value:2}]
for I'm interested in
[{id:2, name:"John"}]
My challenge is that I don't know what the properties are at run time otherwise it would be easy to convert a List<T> to array.
Any suggestion?
To pass list of dynamically generated collection as JSON array to WCF web service, you can use ExpandoObject and any serializer, I have used Newtonsoft.Json
dynamic obj1 = new ExpandoObject();
obj1.id = 1;
obj1.name = "TPS";
dynamic obj2 = new ExpandoObject();
obj2.id = 2;
obj2.name = "TPS_TPS";
var Objects = new List<ExpandoObject>();
Objects.Add(flexible);
Objects.Add(flexible2);
var serialized = JsonConvert.SerializeObject(l); // JsonConvert - from Newtonsoft.Json
To Deserialize and retrieve the object dynamically i have used JavaScriptSerializer, you can also use System.Web.Helpers.Json.Decode()
All you need is this code and reference to a System.Web.Extensions from your project :
public sealed class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
}
#region Nested type: DynamicJsonObject
private sealed class DynamicJsonObject : DynamicObject
{
public readonly IDictionary<string, object> _dictionary;
public DynamicJsonObject(IDictionary<string, object> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}
public override string ToString()
{
var sb = new StringBuilder("{");
ToString(sb);
return sb.ToString();
}
private void ToString(StringBuilder sb)
{
var firstInDictionary = true;
foreach (var pair in _dictionary)
{
if (!firstInDictionary)
sb.Append(",");
firstInDictionary = false;
var value = pair.Value;
var name = pair.Key;
if (value is string)
{
sb.AppendFormat("{0}:\"{1}\"", name, value);
}
else if (value is IDictionary<string, object>)
{
new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
}
else if (value is ArrayList)
{
sb.Append(name + ":[");
var firstInArray = true;
foreach (var arrayValue in (ArrayList)value)
{
if (!firstInArray)
sb.Append(",");
firstInArray = false;
if (arrayValue is IDictionary<string, object>)
new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
else if (arrayValue is string)
sb.AppendFormat("\"{0}\"", arrayValue);
else
sb.AppendFormat("{0}", arrayValue);
}
sb.Append("]");
}
else
{
sb.AppendFormat("{0}:{1}", name, value);
}
}
sb.Append("}");
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!_dictionary.TryGetValue(binder.Name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && indexes[0] != null)
{
if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
return base.TryGetIndex(binder, indexes, out result);
}
private static object WrapResultObject(object result)
{
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
return new DynamicJsonObject(dictionary);
var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
return arrayList[0] is IDictionary<string, object>
? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)))
: new List<object>(arrayList.Cast<object>());
}
return result;
}
}
#endregion
}
How to Use :
string jsonString = ".......";
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
dynamic data = serializer.Deserialize(jsonString, typeof(object));
foreach (var d in data)
{
var id = d.id;
var name = d.name;
}
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
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 }));