c# JavaScriptConverter - how to deserialize custom property? - c#

I've got a class which has been serialized into JSON, and which I'm trying to deserialize into an object.
e.g.
public class ContentItemViewModel
{
public string CssClass { get; set; }
public MyCustomClass PropertyB { get; set; }
}
the simple property (CssClass) will deserialize with:
var contentItemViewModels = ser.Deserialize<ContentItemViewModel>(contentItems);
But PropertyB gets an error...
We added a JavaScriptConverter:
ser.RegisterConverters(new List<JavaScriptConverter>{ publishedStatusResolver});
But when we added 'MyCustomClass' as a 'SupportedType', the Deserialize method was never called. However when we have ContentItemViewModel as the SupportedType, then Deserialize is called.
We've got a current solution which looks something like this:
class ContentItemViewModelConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var cssClass = GetString(dictionary, "cssClass"); //I'm ommitting the GetString method in this example...
var propertyB= GetString(dictionary, "propertyB");
return new ContentItemViewModel{ CssClass = cssClass ,
PropertyB = new MyCustomClass(propertyB)}
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new Exception("Only does the Deserialize");
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new List<Type>
{
typeof(ContentItemViewModel)
};
}
}
}
But we'd prefer a simpler solution of only deserializing MyCustomClass, as there are a number of other fields which are on the ViewModel, and it seems a waste to have to edit this converter every time we change/add a property....
Is there a way to Deserialize JUST PropertyB of type MyCustomClass?
Thanks for your help!

Have you considered using DatacontractJsonSerializer
[DataContract]
public class MyCustomClass
{
[DataMember]
public string foobar { get; set; }
}
[DataContract]
public class ContentItemViewModel
{
[DataMember]
public string CssClass { get; set; }
[DataMember]
public MyCustomClass PropertyB { get; set; }
}
class Program
{
static void Main(string[] args)
{
ContentItemViewModel model = new ContentItemViewModel();
model.CssClass = "StackOver";
model.PropertyB = new MyCustomClass();
model.PropertyB.foobar = "Flow";
//Create a stream to serialize the object to.
MemoryStream ms = new MemoryStream();
// Serializer the User object to the stream.
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ContentItemViewModel));
ser.WriteObject(ms, model);
byte[] json = ms.ToArray();
ms.Close();
string s= Encoding.UTF8.GetString(json, 0, json.Length);
Console.ReadLine();
}
}
Add all possible classes to DatacontractJsonSerializer.KnownTypes if MyCustomClass has derivations.

For whatever it may be worth after all this time, but I stumbled over the same problem and the solution is that the Deserializer hasn't got a clue about the classes you are deserializing unless you give him the necessary information.
On the top level, it knows the type from the type parameter of Deserialize<>(). That's why your converter for ContentItemViewModel works. For nested objects, it needs __type properties and a JavaScriptTypeResolver.
var ser = new JavaScriptSerializer(new SimpleTypeResolver());
ser.RegisterConverters(myconverters);
MyClass myObject = new MyClass();
string json = ser.Serialize(myObject);
// set a breakpoint here to see what has happened
ser.Deserialize<MyClass>(json);
A TypeResolver adds a __type property to each serialized object. You can write a custom type resolver that uses short names. In this sample, I use the SimpleTypeResolver from .net that "simply" stores the fully qualified type name as __type. When deserializing, the JavaScriptDeserializer finds __type and asks the TypeResolver for the correct type. Then it knows a type and can call a registered JavaScriptConverter.Deserialize method.
Without a TypeResolver, objects are deserialized to a Dictionary because JavaScriptSerializer doesn't have any type information.
If you can't provide a __type property in your json string, I think you'll need to deserialize to Dictionary first and then add a "guessing-step" that interprets the fields to find the right type. Then, you can use the ConvertToType method of JavaScriptSerializer to copy the dictionary into the object's fields and properties.
If you need to use the JavaScriptSerializer that is provides by ASP.NET and can't create your own, consider this section from the .ctor help of JavaScriptSerializer:
The instance of JavaScriptSerializer that is used by the asynchronous communication layer for invoking Web services from client script uses a special type resolver. This type resolver restricts the types that can be deserialized to those defined in the Web service’s method signature, or the ones that have the GenerateScriptTypeAttribute applied. You cannot modify this built-in type resolver programmatically.
Perhaps the GenerateScriptType Attribute can help you. But I don't know what kind of __type Properties are be needed here.

Related

How to Override a Default JsonConverter (specified in an attribute)

I would like the following Author type to have a default JsonConverter, and be able to override it at runtime.
[JsonConverter(typeof(BaseJsonConverter))]
public class Author
{
// The ID of an author entity in the application.
public int ID { set; get; }
// The ID of an Author entity in its source.
public string SourceID { set; set; }
}
I used the following code to override the default converter (i.e., BaseJsonConverter).
public class AlternativeConverter : BaseJsonConverter
{ // the serializer implementation is removed for clarity. }
// Deserialize using AlternativeConverter:
var author = JsonConvert.DeserializeObject<Author>(jsonString, new AlternativeConverter());
Question
Using the above call, the AlternativeConverter is first constructed; however, then an instance of BaseJsonConverter is initialized and used for deserialization. So, the AlternativeConverter is never used.
Executable example: https://dotnetfiddle.net/l0bgYO
Use case
The application is to convert different JSON objects, obtained from different sources, to a common C# type. Commonly data comes from a source for that we define the default converter (i.e., BaseJsonConverter), and for data coming from other sources, we define different converters per each.
Background
I am aware of methods such as this one, and indeed I am using similar method partially. With ref to that article, I need to have different _propertyMappings depending on the source of input, because in my application attribute to property mapping is not one-to-one. For instance, I have the following JSON objects:
{
"id":123
}
// and
{
"id":"456"
}
where the first JSON object should be deserialized to:
author.ID = 123
author.SourceID = null
and the second JSON object should be deserialized as:
author.ID = 0
author.SourceID = "456"
You can use a custom ContractResolver to override a [JsonConverter] attribute programmatically. To solve your problem you could make a custom resolver like this:
public class CustomResolver : DefaultContractResolver
{
private Dictionary<Type, JsonConverter> Converters { get; set; }
public CustomResolver(Dictionary<Type, JsonConverter> converters)
{
Converters = converters;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
if (Converters.TryGetValue(objectType, out JsonConverter converter))
{
contract.Converter = converter;
}
return contract;
}
}
Then, when you wanted to use the AlternativeConverter in place of the BaseJsonConverter, you could use the custom resolver like this:
// map the `Author` type to the `AlternativeConverter`
var converters = new Dictionary<Type, JsonConverter>()
{
{ typeof(Author), new AlternativeConverter() }
};
// Create a resolver with the converter mapping and add it to the serializer settings
var settings = new JsonSerializerSettings
{
ContractResolver = new CustomResolver(converters)
};
// Use the settings when deserializing
var author = JsonConvert.DeserializeObject<Author>(jsonString, settings);
Demo Fiddle: https://dotnetfiddle.net/cu0igV
Of course, if all you're really doing with these converters is remapping properties to different names, you could just use a ContractResolver for that in the first place and get rid of the converters altogether. See Json.NET deserialize or serialize json string and map properties to different property names defined at runtime for more information on that approach.
I think you should try to use different JsonSerializerSettings instances for different data sources, with different Converters collections. And remove JsonConverter attributes from your classes.

Deserialize string by class name

Let's say I have a Value that is deserialized from a class.
public class MyValue
{
public string MyPropertyA { get; set; }
public string MyPropertyB { get; set; }
public string DeserializationClass { get; } = typeof(MyValue).Name;
}
I serialize this using JsonConvert class. MyValue class has a property DeserializationClass that should be used as info from which class the string was serialized from. In other words, when I deserialize the string into an object, this property serves as info which class should be used to deserialize the string. However, I am kinda stuck here as I am not sure how to get back the class from the string. Can anybody help me here?
public class Program
{
void Main()
{
var serialized = Serialize();
var obj = Deserialize(serialized);
}
string Serialize()
{
var objValue = new MyValue { MyPropertyA="Something", MyPropertyB="SomethingElse" };
return JsonConvert.SerializeObject<MyClass>(value);
}
object Deserialize(string serialized)
{
//How to deserialize based on 'DeserializationClass' property in serialized string?
return = JsonConvert.Deserialize<???>(serialized);
}
}
EDIT: Modified example to make it more clear what I need as I don't have access to objValue when I need to deserialize the string.
probably you might need to use JsonSerializerSettings.
What you might need to do is
JsonSerializerSettings setting = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
};
and then while serializing use this setting.
var serialized = JsonConvert.SerializeObject(objValue,setting);
this will give you Json like this
{"$type":"WPFDatagrid.MyValue, WPFDatagrid","MyPropertyA":"Something","MyPropertyB":"SomethingElse","DeserializationClass":"MyValue"}
from this you can find the name of the class used it to actually get your type.
Hope this helps !!
There is an overload
If your Type is in form of a Namespace, you can obtain the type from a string representation:
Type objValueType = Type.GetType("Namespace.MyValue, MyAssembly");
object deserialized = JsonConvert.Deserialize(objValueType, serialized);

Deserializing object[] that contains primitive types and one class type

I have an object array that contains strings and longs and this class:
public class SimpleMailAddress
{
public string Address { get; set; }
public string Name { get; set; }
public static implicit operator MailAddress(SimpleMailAddress m)
{
return new MailAddress(m.Address, m.Name);
}
}
However, when deserializing the JSON array with Json.Net, I get an anonymous type that contains Address and Name instead of a SimpleMailAddress object.
I don't want to create a strongly-typed object to deserialize into because it will not be reused and I'd have to create a lot of objects for it. Is there a way to do this with Json.Net or any other library?
This is how I'm serializing/deserializing:
var json = JsonConvert.SerializeObject(myObject);
var myObject = JsonConvert.DeserializeObject<MailMessageRequest>(json);
And MailMessageRequest:
public class MailMessageRequest
{
public string Mailer { get; set; }
public string Method { get; set; }
public object[] Args { get; set; }
}
Json does not contain any inherit knowledge about your SimpleMailAddress class. So when you are telling it to deserialize, the fact that your Args property is of type Object, the deserializer is doing the best it can (by creating an anonymous type). It just sees data, it has no knowledge that you want a SimpleMailAddress object.
Json.net has a JObject class. Try using that instead of Object for your Args parameter if the actual contents of Args may change type.
Then, as needed, you can read the data from the JObject object.
If you don't care about the actual contents of Args, then leave it as Object and ignore it.
Edit: JSon.Net can embed type information during serialization that can be used during deserialization.
Leave your Args parameter as an Object. Then use the TypeNameHandling option of All during both serialization and deserialization.
var json = JsonConvert.SerializeObject(myObject, Formatting.None,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
var myObject = JsonConvert.DeserializeObject<MailMessageRequest>(json,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
You should end up with your Args object as your desired SimpleMailAddress object.
The data contact serializers built into the.net framework have the concept of known types where you tell them what types to expect and it uses those during deserialization.
There is a built in Json data contract serializer but I'm not sure it will be compatible with your Json data, it may need to be serialized and deserialized via a datacontract serializer to work using this method.
Got it. I have to use these settings:
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Full
};
var json = JsonConvert.SerializeObject(object, Formatting.None, settings);
var object = JsonConvert.DeserializeObject<MailMessageRequest>(message.Body, settings);

DataContract Serialization - Can I serialize a Type Property?

I am working on providing a generic JSON serialiation/deserialization service as a WCF service.
in order to do this, I have the following DataContract
[DataContract]
public class SerializationRequest
{
[DataMember]
public Object Data {get;set;} //Object to be serialized
[DataMember]
public string Type {get;set;} //Type of the Data object
}
The problem I am experiencing is I am getting the following error:
The InnerException message was 'Type 'System.RuntimeType' with data contract name 'RuntimeType:http://schemas.datacontract.org/2004/07/System' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
Which tells me I either need to make the DataContractJSONSerializer aware of the type, or that Type is unable to be serialized.
I have tried registering the type of object and adding it to the Serializers "Known Types", but this doesn't seem to work, which makes me believe that my problem lies in having a parameter of type Type on my DataContract.
How can I accomplish this? I need to be able to call the following code("ish").
DataContractJsonSerializer serializer = new DataContractJsonSerializer(request.Type);
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, request.Data);
string json = Encoding.Default.GetString(ms.ToArray());
ms.Dispose();
return new JSONSerializationResponse() {Data = json};
EDIT
I have changed the type parameter to be the Full Qualified Name of the type that is to be serialized, but I am still getting the same problem when I call the following code in my Remote WCF Service:
Type type = Type.GetType(request.Type);
DataContractJsonSerializer serializer = new DataContractJsonSerializer(type, new Type[]{Type.GetType(request.Type)});
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, request.Data);
string json = Encoding.Default.GetString(ms.ToArray());
ms.Dispose();
return new JSONSerializationResponse() {Data = json};
I have tried adding that created type to the KnownTypes in the Constructor of the DataContractJSONSerializer, but that is not working either...any ideas?
Here is an example of a simple class that would be called that needs to be able to be serialized
[DataContract]
[KnownType(typeof(Person))]
public class Person
{
[DataMember]
public string Age {get;set;}
[DataMember]
public strign Name {get;set;}
}
so now I should be able to pass this into my request object with specifying the type name, and then get back a JSON Result of this object.
I'm not sure why you need the .NET type in JSON, but you can serialise the Type to a string and then obviously create a Type from a string.
You can use the Type.FullName and Assembly.GetType("My.Type") to get the string (serialise) and create Type from the name (deserialise).
You're making this way too hard.
Given the following:
[DataContract]
public class Person : BaseObject
{
[DataMember]
public string Age { get; set; }
[DataMember]
public string Name { get; set; }
}
[DataContract]
[KnownType(typeof(Person))]
public class BaseObject
{
}
static string Serialize<T>(T item)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
string result;
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, item);
result = Encoding.Default.GetString(ms.ToArray());
};
return result;
}
You can serialise a Person:
var person = new Person { Name = "Me", Age = Int16.MaxValue.ToString() };
Console.WriteLine(Serialize<Person>(person));
// Produces: {"Age":"32767","Name":"Me"}
In this case the serialiser is working on the basis that the can be deserialised to a single known type. Whoever is going to be pulling this data back out knows what to expect, so the type does not get pushed in.
Or you can serialize a BaseObject:
var person = new Person { Name = "Me", Age = Int16.MaxValue.ToString() };
Console.WriteLine(Serialize<BaseObject>(person));
// Produces: {"__type":"Person:#ConsoleApplication6","Age":"32767","Name":"Me"}
Here the serialiser sees that you're providing a derived type but that type is 'Known' (i.e. both ends know the possible types to expect). So it gets serialized with the type hint. You don't need to do anything more. The framework handles it all for you.
Putting KnownTypes everywhere can be a pain in the butt, so that's where the other overload for the DataContractJsonSerializer constructor comes in handy, so you can specify them at runtime rather than via attributes.
Hope that helps.
You can't work with Type itself, because at runtime it will be an instance of RuntimeType - which is internal class and therefore cannot be added to ServiceKnownType attribute (or to be more precise - cannot be serialized by DataContractSerializer used by WCF).
You should considering serializing type's assembly qualified name as a simple string. This will allow you to recreate type on client side:
[DataContract]
public class SerializationRequest
{
// ...
[DataMember]
public string TypeName {get;set;}
}
// ...
var type = Type.GetType(response.TypeName);
var serializer = new DataContractJsonSerializer(type);

Deserialize json with json.net c#

am new to Json so a little green.
I have a Rest Based Service that returns a json string;
{"treeNode":[{"id":"U-2905","pid":"R","userId":"2905"},
{"id":"U-2905","pid":"R","userId":"2905"}]}
I have been playing with the Json.net and trying to Deserialize the string into Objects etc.
I wrote an extention method to help.
public static T DeserializeFromJSON<T>(this Stream jsonStream, Type objectType)
{
T result;
using (StreamReader reader = new StreamReader(jsonStream))
{
JsonSerializer serializer = new JsonSerializer();
try
{
result = (T)serializer.Deserialize(reader, objectType);
}
catch (Exception e)
{
throw;
}
}
return result;
}
I was expecting an array of treeNode[] objects. But its seems that I can only deserialize correctly if treeNode[] property of another object.
public class treeNode
{
public string id { get; set; }
public string pid { get; set; }
public string userId { get; set; }
}
I there a way to to just get an straight array from the deserialization ?
Cheers
You could use an anonymous class:
T DeserializeJson<T>(string s, T templateObj) {
return JsonConvert.Deserialize<T>(s);
}
and then in your code:
return DeserializeJson(jsonString, new { treeNode = new MyObject[0] }).treeNode;
Unfortunately JSON does not support Type Information while serializing, its pure Object Dictionary rather then full Class Data. You will have to write some sort of extension to extend behaviour of JSON serializer and deserializer in order to support proper type marshelling.
Giving root type will map the object graph correctly if the types expected are exact and not derived types.
For example if I have property as array of base class and my real value can contain derived child classes of any type. JSON does not support it completely but web service (SOAP) allows you to serialize objects with dynamic typing.

Categories

Resources