I'm doing some work for a JIRA REST lib. It uses JSON for communication.
Part of the objects I receive in JSON format are know. The others can be of several different formats. So what I have done is created an object with properties that hide a dictionary
public IssueFields Fields { get; set; }
public IssueType IssueType
{
get { return Fields.IssueType; }
set { Fields.IssueType= value; }
}
IssueFields
{
private Dictionary<string, Field> _fields = new Dictionary<string, Field>();
public string IssueType
{
get { return GetFieldByName<IssueType>(IssueTypeFieldName); }
set { SetFieldByName(IssueTypeFieldName, value); }
}
public T GetFieldByName<T>(string fieldName) where T : class
{
return _fields.ContainsKey(fieldName) ? _fields[fieldName] as T: null;
}
public void SetFieldByName(string fieldName, Field field)
{
if (_fields.ContainsKey(fieldName))
{
_fields[fieldName] = field;
}
else
{
_fields.Add(fieldName, field);
}
}
So I have a bunch of classes like that. I can deserialize into them no problem since JavaScriptSerializer (or any other JSON deserializer) just takes the values and puts them into properties of the objects objects automatically. However there are a bunch of unknown fields all starting with "customField_XXXXX".
What I am currently doing is overriding the JavaScriptSerializer and manually putting EVERYTHING into place. Another idea I got from someone else's code was to re-serialize the dictionary inside the JavaScriptConverter override, then deserialize it into the issue, then put everything else in manually from the dictionary, but that adds a lot of overhead and certainly will raise more than a few eyebrows.
public class MyConverter : JavaScriptConverter
{
private static JavaScriptSerializer _javaScriptSerializer;
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
string json = _javaScriptSerializer.Serialize(dictionary);
Issue issue = _javaScriptSerializer.Deserialize<Issue>(json);
// Then add the rest of my objects manually
Is there any way to get the object back with whatever it could serialize AND the dictionary so I can fill in anything it couldn't on my own? I just haven't been able to find anything other than this method....
Thanks!
Json.Net has a built-in "Extension Data" feature that might solve your problem. After deserializing known properties into your class, it automatically handles adding any "extra" object properties to a dictionary. You can read about it on the author's blog. Also check out this answer for some example code.
Related
I've been working on a C# program that serializes and deserializes some data in JSON format: however, now I have to radically change the type of a pre-existing property, from string to Dictionary(string,string[]).
Is there a way to make it so that, if the program tries to deserialize a document where the field is still a string, it performs some operations to convert it to a Dictionary? Or will the old documents become completely unusable?
For reference, the functions I've been using for reading/writing are the following:
Serialization:
JsonConvert.SerializeObject(this, Formatting.Indented);
Deserialization:
JsonConvert.DeserializeObject<T>(content);
EDIT:
As a simplified version of what I have to do, I have a class with a structure similar to this:
class ExampleClass // V1
{
public string CustomProperty { get; set; }
}
And I need it, after the update, to look like this:
class ExampleClass // V2
{
public Dictionary<string, string[]> CustomProperty { get; set; }
}
What I want is, if I try to deserialize a JSON with the V1 version of the class, instead of raising an exception, that string value will be used to populate the Dictionary (the basic plan is to put a default key and for the array the V1 value split by a default character: I doubt the exact details of the conversion would make much of a difference though). Afterwards, if I try to serialize the resulting object, it will only show the Dictionary, and not the original string.
You could keep two versions of the class you are deserializing into, or you could use a custom JsonConverter and JsonConverterAttribute to deal with the two different ways of deserializing your data.
Either way you choose, you will need some way of identifying what type of document or data type you have, and then you will need an if statement to switch to the appropriate deserialization method. The easiest way of determining this really depends on your situation.
There are some ways on how to handle such a contract change
Keep an old string property, create a new one with another name for the dictionary. Implement the lazy population for the dictionary property
Change the type of the existing property but create a new one and provide [JsonPropertyAttribute] with an old name. Implement the lazy population for the dictionary property
Use JsonConverterAttribute
Have multiple versions of the class (my preferred way)
In the end, I've managed to solve the issue by explicitly writing the deserialization function, like this:
public static ExampleClass DeserializeData(string content)
{
try
{
return JsonConvert.DeserializeObject<ExampleClass>(content);
}
catch
{
// If it's in this section, it's trying to deconvert outdated JSON
Dictionary<string, dynamic> objectMap = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(content);
Dictionary<string, PropertyInfo> propertyMap = new Dictionary<string, PropertyInfo>();
ExampleClass data = new ExampleClass();
foreach (PropertyInfo property in typeof(ExampleClass).GetProperties())
propertyMap.Add(property.Name, property);
foreach (KeyValuePair<string, dynamic> kvp in objectMap)
{
Type propertyType = propertyMap[kvp.Key].PropertyType;
if (kvp.Key != "CustomProperty")
{
if (kvp.Value is Array || kvp.Value is JArray)
{
Type recipientType = propertyType.GetElementType();
Type listType = typeof(List<>).MakeGenericType(recipientType);
System.Collections.IList itemList = (System.Collections.IList)Activator.CreateInstance(listType);
foreach (var item in (kvp.Value as JArray))
itemList.Add(Convert.ChangeType(item, recipientType));
Array itemArray = Array.CreateInstance(recipientType, itemList.Count);
itemList.CopyTo(itemArray, 0);
propertyMap[kvp.Key].SetValue(data, itemArray);
}
else if (Nullable.GetUnderlyingType(propertyType) != null)
{
propertyMap[kvp.Key].SetValue(data, Convert.ChangeType(kvp.Value, Nullable.GetUnderlyingType(propertyType)));
}
else
propertyMap[kvp.Key].SetValue(data, Convert.ChangeType(kvp.Value, propertyType));
}
else
{
if (kvp.Value is string)
{
propertyMap[kvp.Key].SetValue(data, new Dictionary<string, string[]>() { { "DEFAULT", (kvp.Value as string).Split('&') } });
}
else
propertyMap[kvp.Key].SetValue(data, kvp.Value);
}
}
return data;
}
}
With this semi-generic function I can replicate the normal DeserializeObject, and have it elaborate the value depending on its type when it reaches a specific property: not only that, but by having it attempt to use DeserializeObject first, it ensures the fastest alternative is used on properly formatted JSON. It's probably not the most elegant solution, but it works, so I'll take it.
Thank you all for your answers, they were a great help in reaching this solution! Hope this helps others with the same issue as well!
I am trying to combine some Json.NET json serialization with MongoDB.
I have a structure similar to this:
public class Master {
...props...
public Detail[] Details {get;set;}
}
public class Detail {
...props...
public dynamic Value {get;set;}
}
In this case I want the Value property of Detail to contain dynamic json. There is no schema, I just want to store whatever comes in there.
If I receive this structure via e.g. Asp.NET, the Value property will be deserialized as JObject if the value is anything else than a primitive such as string, bool etc.
Storing that into MongoDB will result in the Value property being saved as a JObject. I would just like to store it as the original json structure of the value prop.
There are ways to go from JObject to BsonDocument. e.g. var doc = BsonDocument.Parse(jobj.ToString());.
But in such case, I would have to traverse my object structure and do this manually.
(My real structure is also more complex)
I'm now thinking there might be some better way to deal with this?
e.g. somehow attach a converter of some sort to the value property so that the Mongo driver knows how to turn this into proper Bson.
What are my options here?
I figure it needs to be done on the Bson serializer end of things somehow?
Yes, you need to explicitly implement conversion between JObject and BsonDocument type. You can implement your own converter and use attributes to avoid traversing your C# type structure. Try:
public class Detail
{
[BsonSerializer(typeof(DynamicSerializer))]
public dynamic Value { get; set; }
}
public class DynamicSerializer : SerializerBase<dynamic>
{
public override dynamic Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var myBSONDoc = BsonDocumentSerializer.Instance.Deserialize(context);
return (dynamic)JObject.Parse(context.ToString());
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, dynamic value)
{
var bson = MongoDB.Bson.BsonDocument.Parse(value.ToString());
BsonDocumentSerializer.Instance.Serialize(context, args, bson);
}
}
I have an event that gives me a JSON string:
...
public delegate void DataReceivedHandler(string jsonString);
...
public event DataReceivedHandler OnDataReceived = null;
...
if(OnDataReceived != null)
{
OnDataReceived(jsonString);
}
...
That JSON string could be one of 3 different complex objects: LogOnMessage, LogOffMessage, or DataRequest. Each message has a unique set of fields and properties.
How can I determine which object type the JSON string resolves to?
I know I can write a method that iterates through the JProperty.Name of the JObject and find a match by iterating through my collection of objects and their meta-data, but my gut tells me this is a common challenge to be solved so it must be built in to Newtonsoft JSON .NET somewhere that I simply am overlooking or not understanding. It's probably better and faster than my solution would be too...
I was finally able to detect object type by using JObjects and JsonSchemas.
Steps I Took:
Added a Schema property to my message objects that exposed a _schema field. The first time the property is called, it populates _schema with the return value of JsonSchemaGenerator.Generate(object o).
Convert the JSON string into a JObject via the JObject.Parse() static method.
There is an extension method in Newtonsoft.Json.Schema.Extensions that can compare a JObject to a JsonSchema and determine if they match.
Please note: the methods above have been moved to a separate Newtonsoft.Schema library. So my recommendation is to utilize the latest and greatest library.
private Newtonsoft.Json.Schema.JsonSchema _schema;
public static Newtonsoft.Json.Schema.JsonSchema Schema
{
get
{
if (_schema == null)
{
Newtonsoft.Json.Schema.JsonSchemaGenerator generator = new Newtonsoft.Json.Schema.JsonSchemaGenerator();
_schema = generator.Generate(typeof(DataResponse));
}
return _schema;
}
}
...
Newtonsoft.Json.Linq.JObject message = Newtonsoft.Json.Linq.JObject.Parse(json);
if(Newtonsoft.Json.Schema.Extensions.IsValid(message, DataResponse.Schema))
{...}
else if (Newtonsoft.Json.Schema.Extensions.IsValid(message, ServerStatus.Schema))
{...}
...
I am attempting to save/load a class to an xml file that contains generic types using a DataContractSerializer. I have the save working, but have realized I can't load it because I don't have the list of knownTypes for the deserializer.
Is there a way of serializing/deserializing this class that would allow me to deserialize it without referencing any of the stored types directly?
Here is my SessionVariables class that I am trying to save/load:
[DataContract]
public class SessionVariables
{
[DataMember]
private Dictionary<Type, ISessionVariables> _sessionVariables = new Dictionary<Type, ISessionVariables>();
private object _syncLock = new object();
public T Get<T>()
where T : ISessionVariables, new()
{
lock (_syncLock)
{
ISessionVariables vars = null;
if (_sessionVariables.TryGetValue(typeof(T), out vars))
return (T)vars;
vars = new T();
_sessionVariables.Add(typeof(T), vars);
return (T)vars;
}
}
public IList<Type> GetKnownTypes()
{
IList<Type> knownTypes = new List<Type>();
knownTypes.Add(this.GetType().GetType()); // adds System.RuntimeType
foreach (Type t in _sessionVariables.Keys)
{
if (!knownTypes.Contains(t))
knownTypes.Add(t);
}
return knownTypes;
}
}
The different modules of the application extend the ISessionVariables interface to create their own set of session variables, like this:
[DataContract]
public class ModuleASessionVariables : ISessionVariables
{
[DataMember]
public string ModuleA_Property1{ get; set; }
[DataMember]
public string ModuleA_Property2 { get; set; }
}
[DataContract]
public class ModuleBSessionVariables : ISessionVariables
{
[DataMember]
public string ModuleB_Property1{ get; set; }
[DataMember]
public string ModuleB_Property2 { get; set; }
}
And a singleton instance of the SessionVariables class is used to access session variables, like this:
singletonSessionVariables.Get<ModuleASessionVariables>().ModuleA_Property1
singletonSessionVariables.Get<ModuleBSessionVariables>().ModuleB_Property2
I got the save working like this:
using (FileStream writer = new FileStream(#"C:\test.txt", FileMode.Create))
{
DataContractSerializer dcs = new DataContractSerializer(typeof(SessionVariables), singletonSessionVariables.GetKnownTypes());
dcs.WriteObject(writer, singletonSessionVariables);
writer.Close();
}
However this method does not work to deserialize the class because I don't know it's known types.
Can I serialize and deserialize generic types when I don't have direct library references to any of the types used? And if so, how?
The problem here is that you aren't just wanting to serialize data, but you also want to serialize data about your data, i.e., (cue the dramatic chipmunk) metadata.
That metadata, in this case, are the types of the models that held the data originally. Normally, this isn't an issue, but as you've discovered if you're taking advantage of polymorphism in your design, your single collection may contain two or more different types, each of which needs to be deserialized to their original type.
This is usually accomplished by saving this Type metadata to the serialized result. Different serialization methods does this in different ways. Xaml serialization uses xml namespaces associated with .net namespaces, then names the elements after the original type name. Json.net accomplishes this via a specific named value saved to the json object.
The default DataContractSerializer is not Type aware. Therefore you need to replace it with a version that understands the .NET Type system and can serialize/deserialize Type metadata to the resulting xml. Luckily, one already exists in the framework, the NetDataContractSerializer.
And that's how you pad a link-only answer. The Aristocrats.
You could accomplish this using a custom DataContractResolver. This allows you to plug into the deserialization pipeline and provide a type to deserialize into based upon the type/namespace that is found in the serialized graph.
Here's a good article on it:
http://blogs.msdn.com/b/carlosfigueira/archive/2011/09/21/wcf-extensibility-data-contract-resolver.aspx
IDesign has an implementation of a resolver that can be used for dynamic discovery of types on their site: http://idesign.net/Downloads/GetDownload/1848 (you will probably have to make some modifications to handle generics)
I'm sure this question has been asked over and over again, but for some reason, I still can't manage to get this to work.
I want to deserialize a JSON object that contains a single member; a string array:
[{"idTercero":"cod_Tercero"}]
This is the class that I'm trying to deserialize into:
[DataContract]
public class rptaOk
{
[DataMember]
public string idTercero { get; set; }
public rptaOk() { }
public rptaOk(string idTercero)
{
this.idTercero = idTercero;
}
}
This is the method that I try to deserialize:
public T Deserialise<T>(string json)
{
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(T));
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
T result = (T)deserializer.ReadObject(stream);
return result;
}
}
And so I try to fill the object:
rptaOk deserializedRpta = deserializarOk(rpta);
But for some reason, this returns ""
MessageBox.Show(deserializedRpta.idTercero);
Without any dependencies outside of the .net framework, you could do it this way
[DataContract(Name="rptaOk")]
public class RptaOk
{
[DataMember(Name="idTercero")]
public string IdTercero { get; set; }
}
[CollectionDataContract(Name="rptaOkList")]
public class RptaOkList : List<RptaOk>{}
var stream = new StreamReader(yourJsonObjectInStreamFormat);
var serializer = new DataContractSerializer(typeof(RptaOkList));
var result = (RptOkList) serializer.ReadObject(stream);
I don't know if your're wiling to change the library that you're using, but I use library "Newtonsoft.Json" to desserialize JSON objects, it's pretty easy to use
[HttpPost]
public void AddSell(string sellList)
{
var sellList = JsonConvert.DeserializeObject<List<Sell>>(sellListJson);
BD.SaveSellList(sellList);
}
As you can see you can deserialize a whole json object list to a List<> fo the type "Sell", an object that i've created... And, of course, you can do that to an array too. I don't know the correct syntax for this, but you can convert this list to an array afterwards
Hope this helps
I think you're making this a lot more difficult than it needs to be. Firstly, your sample json and the class you're trying to deserialize into do not have an array of strings. They have a single property of type string. Secondly, why are you using this class DataContractJsonSerializer? You're not doing anything with it that you can't get from a simple call to json.NET's generic deserialization method. I would remove all of your code except the class definition and replace it with this simple one liner;
rptaOk[] myInstances = JsonConvert.DeserializeObject<rptaOk>(jsonString);
Also, no matter what the structure of your json is, if you have a class to correctly model it that method will correctly deserialize it. If you want to enforce some kind of contract I recommend using json schemas which json.NET also supports. If you use a schema it enforces a rigid contract, if you attempt to deserialize into an object there is something of an implicit contract. I don't know every scenario which will cause it to throw, but if your json is too far from the class definition it will. If your class has properties that don't appear in the json I believe they will just get initialized with the default values for that type.
EDIT: I just noticed your json is actually an array of objects. So you simply need to make the lhs of that assignment an array or rptaOk objects, rather than a singleton.