I'm using the DataContractJsonSerializer to deserialize objects from an external service. In most cases, this has worked great for me. However, there is one case where I need to deserialize JSON that contains a list of objects that all inherit from the same base class, but there are many different types of objects in that list.
I know that it can be done easily by including a list of known types in the serializer's constructor, but I don't have access to the code that generated this JSON service. The types that I'm using will be different from the types used in the service (mostly just the class name and namespace will be different). In other words, the classes that the data was serialized with will not be the same classes that I'll use to deserialize it even though they'll be very similar.
With the XML DataContractSerializer, I can pass in a DataContractResolver to map the services types to my own types, but there is no such constructor for the DataContractJsonSerializer. Is there any way to do this? The only options that I've been able to find are: write my own deserializer, or use Microsoft's JsonObject that isn't tested and "should not be used in production environments."
Here is an example:
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Student : Person
{
[DataMember]
public int StudentId { get; set; }
}
class Program
{
static void Main(string[] args)
{
var jsonStr = "[{\"__type\":\"Student:#UnknownProject\",\"Name\":\"John Smith\",\"StudentId\":1},{\"Name\":\"James Adams\"}]";
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
writer.Write(jsonStr);
writer.Flush();
stream.Position = 0;
var s = new DataContractJsonSerializer(typeof(List<Person>), new Type[] { typeof(Student), typeof(Person) });
// Crashes on this line with the error below
var personList = (List<Person>)s.ReadObject(stream);
}
}
}
Here is the error mentioned in the comment above:
Element ':item' contains data from a type that maps to the name
'http://schemas.datacontract.org/2004/07/UnknownProject:Student'. The
deserializer has no knowledge of any type that maps to this name. Consider using
a DataContractResolver or add the type corresponding to 'Student' to the list of
known types - for example, by using the KnownTypeAttribute attribute or by adding
it to the list of known types passed to DataContractSerializer.
I found the answer. It was very simple. I just needed to update my DataContract attribute to specify which namespace (you can also specify a different name) they map to in the source JSON like this:
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject")]
public class Person
{
[DataMember]
public string Name { get; set; }
}
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject"]
public class Student : Person
{
[DataMember]
public int StudentId { get; set; }
}
That JsonObject was a sample for .NET 3.5. There is a project in codeplex - http://wcf.codeplex.com - which has a tested implementation of the JsonValue/JsonObject/JsonArray/JsonPrimitive classes, including source code and unit tests. With that you can parse "untyped" JSON. Another well-used JSON framework is the JSON.NET at http://json.codeplex.com.
You may create a DTO before serializing.
I use a class like: (pseudo code)
class JsonDto
string Content {get;set;}
string Type {get;set;}
ctor(object) => sets Content & Type Properties
static JsonDto FromJson(string) // => Reads a Serialized JsonDto
// and sets Content+Type Properties
string ToJson() // => serializes itself into a json string
object Deserialize() // => deserializes the wrapped object to its saved Type
// using Content+Type properties
T Deserialize<T>() // deserializes the object as above and tries to cast to T
Using the JsonDto you can easily serialize arbitrary objects to JSON and deserialize them to their common base type because the deserializer will always know the original type and returns an type of object reference which will be casted if you use the generic Deserialize<T> method.
One caveat: If you set the Type property you should use the AssemblyQualifiedName of the type, however without the version attribute (ex: MyCompany.SomeNamespace.MyType, MyCompany.SomeAssembly). If you just use the AssemblyQualifiedName property of the Type class you will end up with errors if your assembly version changes.
I implemented a JsonDtoCollection the same way, which derives from List<JsonDto> and provides methods to handle collections of objects.
class JsonDtoCollection : List<JsonDto>
ctor(List<T>) => wraps all items of the list and adds them to itself
static JsonDtoCollection FromJson(string) // => Reads a collection of serialized
// JsonDtos and deserializes them,
// returning a Collection
string ToJson() // => serializes itself into a json string
List<object> Deserialize() // => deserializes the wrapped objects using
// JsonDto.Deserialize
List<T> Deserialize<T>() // deserializes the as above and tries to cast to T
Related
I'm trying to deserialize a JSON object into an Object with some "object" attribute that may be different each time. I have a serializer/deserializer function that works fine when using simple variable types or defined ones.
I tried to cast the object into the correct class, tried to get the object as dynamic, etc. However, I always get an exception: "Object Reference not established..."
Deserialization func:
public static T deserializeJSON<T>(string json)
{
T obj = Activator.CreateInstance<T>();
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType($
obj = (T)serializer.ReadObject(ms);
}
return obj;
}
Example object:
[DataContract]
class Client
{
[DataMember] private string name;
[DataMember] private string address;
[DataMember] private string bio;
[DataMember] private object specific; //WHERE SPECIFIC MAY BE ANY OTHER OBJECT THAT I CAST AFTER DESERIALIZATION
}
Example object2:
[DataContract]
class Server
{
[DataMember] private string name;
[DataMember] private int value;
}
The specific attribute may be any other object. Imagine "specific" attribute is a Server type object; The deserialization function loads all attributes well instead of specific that is loaded as an object, but cannot convert it to Server.
PD: At deserialization moment, I know what class type is the "specific" attribute.
Thanks!
using latest json.net you can use
dynamic data = Json.Decode(json);
or
dynamic data = JObject.Parse(json);
and can access data with following code :
data.Date;
data.Items.Count;
data.Items[0].Name;
data.Items[0].Price;
data.Items[1].Name;
I have the following Console application:
using System;
using System.IO;
using System.Xml.Serialization;
using Newtonsoft.Json;
namespace OutputApp
{
public class Foo
{
public object Value1 { get; set; }
public string Value2 { get; set; }
}
public class Bar
{
public int Arg1 { get; set; }
public double Arg2 { get; set; }
}
class Program
{
public static Foo CreateFooBar()
{
return new Foo
{
Value1 = new Bar
{
Arg1 = 123,
Arg2 = 99.9
},
Value2 = "Test"
};
}
public static string SerializeXml(object obj)
{
using (var stream = new MemoryStream())
{
using (var reader = new StreamReader(stream))
{
var serializer = new XmlSerializer(obj.GetType());
serializer.Serialize(stream, obj);
stream.Position = 0;
return reader.ReadToEnd();
}
}
}
static void Main(string[] args)
{
var fooBar = CreateFooBar();
// Using Newtonsoft.Json
var json = JsonConvert.SerializeObject(fooBar, Formatting.Indented);
var xnode = JsonConvert.DeserializeXNode(json, "RootElement");
var xml = xnode.ToString();
// Using XmlSerializer, throws InvalidOperationException
var badXml = SerializeXml(fooBar);
Console.ReadLine();
}
}
}
I have two classes. Class Foo and class Bar. Class Foo has a property of type object. This is a requirement, because it is a contract which can hold a variety of objects and therefore I cannot set the property to a concrete type or a generic.
Now I compose a dummy fooBar object using the CreateFooBar() method. After that I first serialize it into JSON, which works wonderfully with Json.Net.
Then I use Json.Net's XML converter method to convert the json string into an XNode object. It works great as well.
The output of both is the following:
{
"Value1": {
"Arg1": 123,
"Arg2": 99.9
},
"Value2": "Test"
}
<RootElement>
<Value1>
<Arg1>123</Arg1>
<Arg2>99.9</Arg2>
</Value1>
<Value2>Test</Value2>
</RootElement>
Now while this works, it is certainly not very nice, because I have to serialize into json only to serialize it into xml afterwards. I would like to serialize directly into xml.
When I use the XmlSerializer to do this I get the infamous InvalidOperationExceptoin, because I did not decorate my classes with the XmlInclude attribute or did one of the other workarounds.
InvalidOperationException
The type OutputApp.Bar was not expected. Use the XmlInclude or
SoapInclude attribute to specify types that are not known statically.
None of the workarounds for the XmlSerializer is a good solution IMHO and I don't see the need for it as it is perfectly feasible to serialize an object into XML without crappy attributes.
Does anyone know a good Xml serializer in .NET which can do this or is there a plan to add this feature to Json.Net?
Any ideas?
Update1
I am not opposed to use attributes, but it needs to make sense. What I don't like about the XmlInclude attribute is that it forces me into circular dependencies. Say I have assembly A which defines a base class, and assembly B which implements derived classes. Now the way the XmlInclude attribute works is that I'd have to decorate the base class in Assembly A with the type name of the child class from assembly B. This would mean I have a circular dependency and is a no go!
Update2
I shall clarify that I am not looking for a solution to re-factor my console application to make it work with the XmlSerializer, I am looking for a way to XML serialize what I have there.
There was a comment below which mentions that using object as a data type is poor design. Whether this is true or not, this is a whole other discussion. The point is that there is no reason why it shouldn't be able to serialize into XML and I am curious to find such a solution.
Personally I find creating a "marker" interface a dirty design. It abusing an interface to workaround the incapabilities of one single .NET class (XmlSerializer). If I would ever swap the serialization library for something else, then the whole marker interface would be redundant clutter. I don't want to couple my classes to one serializer.
I am looking for an elegant solution (if there is one)?
You don't need to pollute your models with XmlInclude attributes. You could explicitly indicate all known classes to the XmlSerializer's constructor:
var serializer = new XmlSerializer(obj.GetType(), new[] { typeof(Bar) });
Also using object as base class seems like a crappy approach. At least define a marker interface:
public interface IMarker
{
}
that your Bar's will implement:
public class Bar : IMarker
{
public int Arg1 { get; set; }
public double Arg2 { get; set; }
}
and then then specialize the Value1 property of your Foo class to this marker instead of making it like the most universal type in the universe (it can't be):
public class Foo
{
public IMarker Value1 { get; set; }
public string Value2 { get; set; }
}
Coz now it's pretty trivial to get all loaded types at runtime in all referenced assemblies that are implementing the marker interface and passing them to the XmlSerializer constructor:
var type = typeof(IMarker);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p != type)
.Where(p => type.IsAssignableFrom(p))
.ToArray();
var serializer = new XmlSerializer(obj.GetType(), types);
Now you've got a pretty capable XmlSerializer that will know how to properly serialize all types implementing your marker interface. You've achieved almost the same functionality as JSON.NET. And don't forget that this XmlSerializaer instantiation should reside in your Composition Root project which knows about all loaded types.
And once again using object is a poor design decision.
I have to extract a part of json-string using .net or newtonsoft json.
JSON:
var json = "{\"method\":\"subtract\",\"parameters\":{\"minuend\":\"SOME_CUSTOM_JSON_OBJECT_DIFFERENT_FOR_EACH_METHOD\",\"subtrahend\":23}}";
C# Class:
class MyJson{
public string method { get; set; }
//public string parameters {get; set;}
public object parameters {get; set;}
}
I do not need to parse all the children of "parameters" json-object. "parameters" could be a very big object ([{obj1}...{obj1000}], objX of 1000 fields), parse which would be not performant.
I would like i.e. to pass it exactly as it is on some point, so conversion "string-C#object-string" would be redundant.
I do not want use Regexp or string transformations (string.Substring, Split and co), because of error margin, I know that all .net and newtonsoft string transformations based.
Question 1: if I define a property of type "object", how newtonsoft will handle this? (Documentation is worse than msdn, so I'm looking for the input from you, who already tried this).
static void Main(string[] args)
{
var json = "{\"method\":\"subtract\",\"parameters\":{\"minuend\":42,\"subtrahend\":23}}";
var data = JsonConvert.DeserializeObject<MyJson>(j);
// what internal representaion of data.parameters?
// How is it actually converted from json-string to an C# object (JObject/JsonObject).
}
In perfect case:
"parameters" is a string and calling
ExtractMyJson(jsonString)
gives me the json string of parameters.
Basically I need the newtonsoft version of
string ExtractMyJson(jsonString){
var p1 = jsonString.Split(",");
// .. varios string transformations
return pParams;
}
Note: please don't reference "dynamic" keyword or ask why no string transformations, it's the very specific question.
If you know that your parameters are unique you can do something like this:
class MyJson
{
public string method { get; set; }
public Dictionary<string,object> parameters { get; set; }
}
................
string json = "{\"method\":\"subtract\",\"parameters\":{\"minuend\":{\"img\": 3, \"real\": 4},\"subtrahend\":23}}";
var data = JsonConvert.DeserializeObject<MyJson>(json);
If you let it as object is going to receive the type Newtonsoft.Json.Linq.JObject.
Have you tried JTOKEN?
It is a rather simple solution to partially read basic or nested JSONs as described in this post.
For a nested JSON
{
"key1": {
"key11": "value11",
"key12": "value12"
}
"key2": "value2"
}
it would look like this
JToken token = JToken.Parse(json);
var value12 = token.SelectToken("key1.key12");
to get the element of the key "key12.
I think this could go nicely with your problem.
Well Objects are treated the same way your parent object is treated. It will start from the base of the graph. So if you have something like:
Person
{
Address Address {get;set;}
}
The Json will start Deserializing Address and then add in the Person object.
If you want to limit thesize of the graph depth you can use a setting like :
JsonConvert.DeserializeObject<List<IList<IList<string>>>>(json, new JsonSerializerSettings
{
MaxDepth = 2
});
For more configurations of the JsonSerializer check JsonSerializerSettings
If your field is an object then that object will have the KeyValuePair of every property that it holds, based on that when you cast that field you can access that type.(the behaviour is the same as assigning a type to an object in C#).
Update: So if you question using JsonObject or type, well JObject is and intermediary way to construct the json format in a generic format. But using the Type deserializatin means you can ignore properties you are not interested in. Mapping to a json with a type makes more sense because it creates a new object and dismisses the old JObject.
I'm already knew how to deserialize a JSON array using DataContractJsonSerializer, but now I have a JSON object, it has a key called items and the value is an array.
for example, the JSON string is { name: "someString", items: [1, 2, 3] }
Now I want to know is there any built-in method to convert this object to a custom class derived from List<T>, for example to the following class:
[DataContract] // This is not working, you can't apply DataContract attribute to a List<T>
// [ItemsCollectionIsIn("items")]
public class MyObject : List<int>
{
[DataMember]
public string name;
}
I had tried [CollectionDataContract] attribute with its ItemName set to "items", but seems DataContractJsonSerializer just ignores ItemName property and serializes/deserializes the object to/from an array.
Now I can implement IList<T> on my class and return values from a internal list, or I think I can write one class for JSON object contract and the other one derived from List then populate values manually, is there any better ways?
I'm working on Windows Phone LongListSelector so I need a GroupedList (List<List<T>>).
Or if JSON.Net can do it more easier please give me some hint.
I think you modeled your type incorrectly, the array is a property in the root object. Try it this way:
[DataContract]
public class MyObject
{
[DataMember]
public string name;
[DataMember]
public List<int> items;
}
I have a base class with some properties like:
public class BaseClass {
public string Name { get; set; }
public string Comment { get; set; }
}
And classes who extend the base class like:
public class A : BaseClass {
public string AProperty{ get; set; }
}
and
public class B : BaseClass {
public string BProperty{ get; set; }
}
Now I need to be able to serialize the data from the extension and from the base class into different Locations.
And for deserialization I need to be able to deserialize one of the classes and "merge" with base class who was deserialized earlier.
The baseclass is mine so I can change it as needet, but not the extension classes (A and B in my sample).
It would be nice to have a solution that works with XML and JSON (Newtonsoft JSON serializer) serializer.
Thanks for ideas.
I do not know of a way to do this with XML, but with Newtonsoft JSON.NET:
You cannot merge the deserialized result unless you know the final target type first. This probably means you will have to include the type name in your JSON in some way either automatically via TypeNameHandling or manually.
If you chose to use TypeNameHandling, you can create the object by calling Deserialize(JsonReader) and let it create an object of the proper type.
If you stored the type in some other way, you can create the object by using the overload which takes a type: Deserialize(JsonReader, Type)
Once you have the correct object, you can then add the additional properties from the second fragment via Populate.
Note: If you don't know the type of the object until the second fragment, why not just keep the first fragment as JSON and parse the second fragment first? Then add the first fragment via Populate. It really doesn't matter in which order the fragments are deserialized as long as they don't have any overlapping fields.
To perform the serialization, you will need to use a custom IContractResolver to filter the fields. For example, something like this would allow filtering to all properties that are declared in a particular type (Warning: I did not attempt to compile this):
public class DeclaredMembersResolver<T> : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> filtered = new List<JsonProperty>();
foreach (JsonProperty p in base.CreateProperties(type, memberSerialization))
if(p.DeclaringType == typeof(T))
filtered.Add(p);
return filtered;
}
}
// Example of use:
var ser = JsonSerializer.CreateDefault(new JsonSerializerSettings() {
ContractResolver = new DeclaredMembersResolver<BaseClass>()
});
ser.Serialize(writer, obj); // Only the base properties of obj will be written
Obviously you would then extend the above to do the inverse (exclude base class properties) to serialize your second JSON block.
What you want to do can be done with Newtonsoft's JSON library with a bit of work by using JObject and Linq to query for properties of the deserialized object and manually doing what you want.
Further info can be found here: http://james.newtonking.com/json/help/index.html
As an example: If you have a baseObject that has been already deserialized and a string containing the data:
JObject jObj = JObject.Parse(json);
A aObj = new A();
aObj.Name = baseObject.Name;
aObj.Comment = baseObject.Name;
a.AProperty = (string)jObj[propertyInfo.Name];
If you do not know the additional properties of objects, but have access to the types at runtime that can easily be found through reflection. Example continuing from above
var t = typeof(A);
PropertyInfo[] pi = t.GetProperties();
foreach (var propertyInfo in pi)
propertyInfo.SetValue(aObject, (string)jObj["AProperty"]);