So, what I want is to serialise and deserialise some JSON using a generic field wrapper class.
I do not want to write some custom JsonConverter class.
I do wish to keep it simple and write some implicit operator type conversion. The following minimal piece of C# code is where I am at now .... just enough to demonstrate the issue and no more. Don't overlook the implicit operators.
using Newtonsoft.Json;
using System;
namespace test
{
public struct SofT<T>
{
[JsonProperty]
public T TValue { get; set; }
public static implicit operator SofT<T>(string jtoken)
{
return new SofT<T>()
{
TValue = (T)Convert.ChangeType(jtoken, typeof(T))
};
}
public static implicit operator string(SofT<T> soft)
{
return soft.TValue?.ToString() ?? "";
}
}
[JsonObject(MemberSerialization.OptIn)]
public class Something
{
[JsonProperty]
public SofT<int> TestStructInt { get; set; }
[JsonProperty]
public SofT<decimal> TestStructDecimal { get; set; }
}
public class Program
{
public void Run()
{
var json = "{ \"TestStructInt\" : \"12\", \"TestStructDecimal\" : \"3.45\"}";
var modelDeserialised = JsonConvert.DeserializeObject<Something>(json);
var modelReserialised = JsonConvert.SerializeObject(modelDeserialised);
Console.WriteLine(modelReserialised);
}
static void Main(string[] args)
{
new Program().Run();
}
}
}
The JSON string is deserialised perfectly.
The model object is not reserialised correctly.
The string that is spat out to the console is:
quote {"TestStructInt":{"TValue":12},"TestStructDecimal":{"TValue":3.45}}
The string I expect, or better put want, to be spat out to the console is the same structure in the source JSON, ie:
quote {"TestStructInt":"12"},"TestStructDecimal":"3.45"}}
I am asking for a second pair of eyes to point out my error (and yes I can see the error, the annotation of Value with [JsonProperty], but it seems necessary for default serialisation).
Related
I'm trying to deserialize a piece of XML that specifies a .NET type to an instance of System.Type. Given
<SomeObject>
<SomeType>System.String, mscorlib</SomeType>
</SomeObject>
To deserialize to a class;
public class SomeObject
{
public Type SomeType { get; set; }
}
Annoyingly, I've actually done this before a while back but without access to that source code and not being able to remember, this has proven very difficult to research the solution given the keywords needed ("Xml", "Deserialize", "Type" gives pretty much everything under the sun).
From what I remember, there is a simple Attribute that I put on the SomeType property and the XmlSerializer takes care of it from there. Does anyone know what attribute I need or am I mis-remembering?
If you don't want to have additional property of type string (usual solution to this problem) - you can use proxy class like this:
public class XmlTypeProxy : IXmlSerializable {
private string _typeName;
public XmlTypeProxy() {
}
public XmlTypeProxy(string typeName) {
_typeName = typeName;
}
public XmlSchema GetSchema() {
return null;
}
public void ReadXml(XmlReader reader) {
_typeName = reader.ReadString();
}
public void WriteXml(XmlWriter writer) {
writer.WriteString(_typeName);
}
public static implicit operator Type(XmlTypeProxy self) {
return Type.GetType(self._typeName);
}
public static implicit operator XmlTypeProxy(Type self) {
return new XmlTypeProxy(self.AssemblyQualifiedName);
}
}
What this class does is just stores type assembly qualified name as string and defines implicit conversion operator from and to Type type. Then you just need to decorate SomeType with XmlElement attribute and specify it's Type is XmlTypeProxy:
public class SomeObject {
[XmlElement(Type = typeof(XmlTypeProxy))]
public Type SomeType { get; set; }
}
Now, because there is implicit converstion from Type to XmlTypeProxy (and visa versa) - both serialization and deserialization will work as you expect.
While I've marked Evk's answer as correct (and it is if you are deserializing more than one Type property), I actually went with a simpler approach in the end.
Based on this answer, I modified by SomeObject to;
public class SomeObject
{
public string SomeTypeName
{
get { return SomeType.AssemblyQualifiedName; }
set
{
var converter = new TypeNameConverter();
SomeType = (Type)converter.ConvertFrom(value);
}
}
[XmlIgnore]
public Type SomeType { get; set; }
}
While a shorter piece of code for a single property, it's not as robust as the accepted answer. I'm recording here has the two approaches may help others.
I've used https://quicktype.io to create a class for my Json data. However I'm not really understanding why with the helper methods created to convert to json, from json, and supply default parameters they are each created differently as a static method of the class, a static class and a class with a static method.
The reason really is because as soon as I create a second class from another set of json data the code fails because of the static classes and I want to make sure I refactor them correctly. As well as understand the reasons of course.
I figure 'Converter' will never change across all my json objects so I can move this as is to a separate file and Serialize to a static method with FromJson. But I'd just like to understand more about the reasoning of how it was done in the first place and the better approach.
Here is the code:
public partial class StationDO
{
public string Active { get; set; }
//more fields here
}
public partial class StationDO
{
public static List<StationDO> FromJson(string json)
{
return JsonConvert.DeserializeObject<List<StationDO>>(json, Converter.Settings);
}
}
public static class Serialize
{
public static string ToJson(this List<StationDO> self)
{
return JsonConvert.SerializeObject(self, Converter.Settings);
}
}
public class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
};
}
If I look at the Java code the same site produces, it simply puts everything bar the bean into a single class 'Converter'
You can have all the static members in StationDO class. In that case I recommend mark that class as sealed (public sealed class StationDO) to prevent someone to inheriting from that class and use the static methods from inherited class.
public class InheritedStationDO : StationDO { }
// ... somewhere else ...
InheritedStationDO.FromJson(jsonValue); // still returns List<StationDO> not List<InheritedStationDO> !!!
EDIT:
After close look I think, the whole design of members is not good.
1) There is no need to accept just List<StationDO>.
2) There is no need the defined special methods for (de)serialization of every class, you will have. You can have just one method for serialization and one for deserialization for all your classes.
Example:
public class StationDO {
public string Active { get; set; }
}
public class AnotherDO {
public string Name { get; set; }
}
// and more *DO classes
// class need to be "static" because contains "extension methods"
public static class MySerializationHelper {
private static readonly JsonSerializerSettings serializationSettings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
};
// universal method for deserialization from json
// the generic type "T" represents the result type of deserialization
public static T FromJson<T>(string json) {
return JsonConvert.DeserializeObject<T>(json, serializationSettings);
}
// universal method for serialization to json
// this "this" keyword means, its "extension method"
public static string ToJson<T>(this T self) {
return JsonConvert.SerializeObject(self, serializationSettings);
}
}
Usage:
StationDO obj01 = GetSomeStation();
// returns json of one object
string json01A = obj01.ToJson(); // these two lines are equivalent
string json01B = MySerializationHelper.ToJson(obj01); // these two lines are equivalent
// returns new object deserialized from json in "json01" variable
StationDO clone01 = MySerializationHelper.FromJson<StationDO>(json01A);
StationDO obj02 = GetAnotherStation();
StationDO[] arr01 = new StationDO[] { obj01, obj02 };
// returns json of array with two objects
string json02A = arr01.ToJson(); // these two lines are equivalent
string json02B = MySerializationHelper.ToJson(arr01); // these two lines are equivalent
// returns new array with deserialized object from json in "json02" variable
StationdDO[] clone02 = MySerializationHelper.FromJson<StationdDO[]>(json02A);
AnotherDO obj03 = GetAnotherDO();
string json03A = obj03.ToJson(); // these two lines are equivalent
string json03B = MySerializationHelper.ToJson(obj03); // these two lines are equivalent
As you see, the generics is the way, how to avoid code duplication for every class.
And you can (de)serialize all kind of arrays and collections or single objects. Not just List<T>.
I have a set of complex business objects that I'd like to serialize to Json for use in a web service. I'm currently using the DataContractJsonSerializer to product the Json, but it balks on deserialization because the default XmlReader can't handle Base64 strings.
After reading many positive reviews of Json.Net, I decided to give it a try. Surprisingly, the simplest case produces erroneous output if the business object overrides the ToString() method. Instead of generating JSON, it simply emits the string value.
For example, the following statement produces only a string, as the serializer appears to view the object as a simple string.
public class MyClass {
public string Title{get;set;}
public override ToString(){ return Title; }
public string ToJson(){
return JsonConvert.SerializeObject(this);
}
}
Instead of json formatted output, all I get is the title string. Do I have to mark the object in some special way to avoid this? Since the business object hierarchy includes many objects that override ToString(), I would rather avoid having to introduce special attributes, etc.
Is it possible that your actual class has a TypeConverterAttribute attached to it? I just ran into the exact same problem and found out that the TypeConverterAttribute was causing this. In thast case, this might help (at least it did for me).
This is very bad because you can inadvertently break your program (by simply adding a TypeConverter maybe for displaying the object in a PropertyGrid) without getting a compiler warning...
using Newtonsoft.Json;
using System;
using System.ComponentModel;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
var with = new WithTypeConverter() { Bla = 12, Blub = "With" };
var without = new WithoutTypeConverter() { Bla = 12, Blub = "Without" };
Console.WriteLine(with);
Console.WriteLine(JsonConvert.SerializeObject(with));
Console.WriteLine(without);
Console.WriteLine(JsonConvert.SerializeObject(without));
Console.ReadKey();
}
}
public class baseClass
{
public int Bla { get; set; }
public string Blub { get; set; }
public override string ToString()
{
return String.Format("{0}: {1}", this.GetType().Name, Blub);
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public class WithTypeConverter : baseClass
{
}
public class WithoutTypeConverter : baseClass
{
}
}
You may be testing this wrong. I just ran the following code in LINQPad:
void Main()
{
new MyClass{Title = "hi"}.ToJson().Dump();
}
// Define other methods and classes here
public class MyClass {
public string Title{get;set;}
public override string ToString(){ return Title; }
public string ToJson(){
return JsonConvert.SerializeObject(this);
}
}
Output:
{"Title":"hi"}
I suspect you are using MyClass as the key in a dictionary or hashtable?
Linqpad example:
void Main()
{
object thing = new Dictionary<MyClass, MyClass>() {
{
new MyClass { Title = "hi" }, new MyClass { Title = "bye" }
}
};
JsonConvert.SerializeObject(thing).Dump();
}
public class MyClass
{
public string Title { get; set; }
public override string ToString() { return "BROKEN"; }
}
Output:
{"BROKEN":{"Title":"bye"}}
This is the expected behaviour as there is no way to express a complex object as a key in json.
To work around this either change your model or implement a TypeConverter.
If your object is simple enough you could have the ConvertTo and ConvertFrom simply read and write parameters in a given order.
[EDIT]
This turned out to be more simple than I'd expected. Here is my JsonConverter solution.
public class ObjectKeyDictionaryTypeConverter<T1, T2> : JsonConverter<Dictionary<T1, T2>>
{
public override void WriteJson(JsonWriter writer, Dictionary<T1, T2> value, JsonSerializer serializer)
{
serializer.Serialize(writer, value.ToArray());
}
public override Dictionary<T1, T2> ReadJson(JsonReader reader, Type objectType, Dictionary<T1, T2> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var items = serializer.Deserialize(reader) as KeyValuePair<T1,T2>[];
return items?.ToDictionary(a => a.Key, a => a.Value);
}
}
Usage:
[JsonConverter(typeof(ObjectKeyDictionaryTypeConverter<ICriteria, Results>))]
public Dictionary<ICriteria, Results> SearchesAndResults { get; set; }
Use JsonSerializer from System.Text.Json to serialize the class. Like so:
using System.Text.Json;
...
public class Foo{
Public String Title {get;set;}
public override ToString(){
return JsonSerializer.Serialize<Foo>(this);
}
}
Docs:
https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer?view=netcore-3.1
Context:
https://youtu.be/JfnTG955cuk?t=406
I'm attempting to write a set of classes to represent a particularly complex object, and in one of those classes, I have a property that is set as the base (abstract) class of three possible derived classes. I'm setting up an ASP.NET Web API to handle the serialization and deserialization, which means that, by default, it uses Json.NET for JSON. How can I get the Web API to properly deserialize JSON sent via POST or PUT into the proper derived class?
The class with the abstract member looks like this (I'm including the Xml decorators for clarity and because they work perfectly well for deserializing xml using the XmlSerializer)
[Serializable]
public class FormulaStructure {
[XmlElement("column", typeof(ColumnStructure))]
[XmlElement("function", typeof(FunctionStructure))]
[XmlElement("operand", typeof(OperandStructure))]
public AFormulaItemStructure FormulaItem;
}
The abstract class is pretty basic:
[Serializable]
public abstract class AFormulaItemStructure { }
And there are three derivatives of the abstract class:
[Serializable]
public class ColumnStructure: AFormulaItemStructure {
[XmlAttribute("type")]
public string Type;
[XmlAttribute("field")]
public string Field;
[XmlAttribute("display")]
public string Display;
}
[Serializable]
public class FunctionStructure: AFormulaItemStructure {
[XmlAttribute("type")]
public string Type;
[XmlAttribute("name")]
public string Name;
[XmlElement("parameters")]
public string Parameters;
}
[Serializable]
public class OperandStructure: AFormulaItemStructure {
[XmlAttribute("type")]
public string Type;
[XmlElement("left")]
public string Left;
[XmlElement("right")]
public string Right;
}
At present, using [DataContract] attributes, the Json.NET formatter fails to populate the derived class, leaving the property null.
Questions
Can I mix XmlSerializer attributes with DataContractSerializer attributes on the same class? I use the XmlSerializer because I use xml attributes in the xml I designed, but that can be changed if necessary since I am developing the xml schema myself.
What is the equivalent in Json.NET to [KnownType()] ? Json.NET doesn't appear to respect the DataContractSerializer version of KnownType. Will I need to roll my own JsonConverter to determine the proper type?
How would I decorate the classes so that DataContractSerializer or DataContractJsonSerializer will properly deserialize the objects in both Xml and Json? My goal is to put this into an ASP.NET Web API, so I want the flexibility to generate Xml or Json, as appropriate to the requested type. Is there an alternative formatter that I need to use to work with this complex class, if Json.NET won't work?
I need the ability to generate an object on the client side without necessarily including the .NET class names into the object.
Testing and Refinement
In my testing of the Web API, the default serialization sends down to the client:
{"FormulaItem":{"type":"int","field":"my_field","display":"My Field"}}
which is ideal for my purposes. Getting this to go back to the API and deserialize into the proper derived types, though, isn't working (it's generating null for the property).
Testing Tommy Grovnes answer below, the DataContractSerializer he used for testing generates:
{"FormulaItem":{"__type":"column:#ExpressionStructureExperimentation.Models","display":"My Field","field":"my_field","type":"int"}}
which doesn't work for me, or for code maintainability (refactoring becomes a PITA if I hard-code the entire namespace into the JavaScript for generating these objects).
You can mix as mentioned already but I don't think you need to, haven't used WEB api myself but WCF Rest produces xml and json from DataContracts (without Xml.. tags), tag your classes like this:
[DataContract]
public class FormulaStructure
{
[DataMember]
public AFormulaItemStructure FormulaItem;
}
[DataContract]
[KnownType(typeof(ColumnStructure))]
[KnownType(typeof(FunctionStructure))]
[KnownType(typeof(OperandStructure))]
public abstract class AFormulaItemStructure { }
[DataContract(Name="column")]
public class ColumnStructure : AFormulaItemStructure
{
[DataMember(Name="type")]
public string Type;
[DataMember(Name = "field")]
public string Field;
[DataMember(Name = "display")]
public string Display;
}
[DataContract(Name="function")]
public class FunctionStructure : AFormulaItemStructure
{
[DataMember(Name = "type")]
public string Type;
[DataMember(Name = "name")]
public string Name;
[DataMember(Name = "parameters")]
public string Parameters;
}
[DataContract(Name = "operand")]
public class OperandStructure : AFormulaItemStructure
{
[DataMember(Name = "type")]
public string Type;
[DataMember(Name = "left")]
public string Left;
[DataMember(Name = "right")]
public string Right;
}
If you need more control over the XML/JSON generated you might have to tweak this further. I used this code to test:
public static string Serialize(FormulaStructure structure)
{
using (MemoryStream memoryStream = new MemoryStream())
using (StreamReader reader = new StreamReader(memoryStream))
{
var serializer = new DataContractSerializer(typeof(FormulaStructure));
serializer.WriteObject(memoryStream, structure);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
public static FormulaStructure Deserialize(string xml)
{
using (Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
var deserializer = new DataContractSerializer(typeof(FormulaStructure));
return (FormulaStructure)deserializer.ReadObject(stream);
}
}
After we ran into some issues much further down the line with my previous answer, I discovered the SerializationBinder class that JSON can use for serializing/deserializing namespaces.
Code First
I generated a class to inherit the SerializationBinder:
public class KnownTypesBinder : System.Runtime.Serialization.SerializationBinder {
public KnownTypesBinder() {
KnownTypes = new List<Type>();
AliasedTypes = new Dictionary<string, Type>();
}
public IList<Type> KnownTypes { get; set; }
public IDictionary<string, Type> AliasedTypes { get; set; }
public override Type BindToType(string assemblyName, string typeName) {
if (AliasedTypes.ContainsKey(typeName)) { return AliasedTypes[typeName]; }
var type = KnownTypes.SingleOrDefault(t => t.Name == typeName);
if (type == null) {
type = Type.GetType(Assembly.CreateQualifiedName(assemblyName, typeName));
if (type == null) {
throw new InvalidCastException("Unknown type encountered while deserializing JSON. This can happen if class names have changed but the database or the JavaScript references the old class name.");
}
}
return type;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName) {
assemblyName = null;
typeName = serializedType.Name;
}
}
How it works
Let's say I have a set of classes defined thus:
public class Class1 {
public string Text { get; set; }
}
public class Class2 {
public int Value { get; set; }
}
public class MyClass {
public Class1 Text { get; set; }
public Class2 Value { get; set; }
}
Aliased Types
What this does is allows me to generate my own names for classes that will be serialized/deserialized. In my global.asax file, I apply the binder as such:
KnownTypesBinder binder = new KnownTypesBinder()
binder.AliasedTypes["Class1"] = typeof(Project1.Class1);
binder.AliasedTypes["WhateverStringIWant"] = typeof(Project1.Class2);
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.Binder = binder;
Now, whenever I serialize, say, MyClass as JSON, I get the following:
{
item: {
$type: "Project1.MyClass",
Text: {
$type: "Class1",
Text: "some value"
},
Value: {
$type: "WhateverStringIWant",
Value: 88
}
}
}
Known Types
I can also choose to strip off the assembly information and strictly use the class name by adding information to the KnownTypesBinder:
KnownTypesBinder binder = new KnownTypesBinder()
binder.KnownTypes.Add(typeof(Project1.Class1));
binder.KnownTypes.Add(typeof(Project1.Class1));
In the two examples given, Class1 is referenced the same way. However, if I refactor Class1 to, say, NewClass1, then this second example will start sending a different name. That may or may not be a big deal, depending on whether you are using the types or not.
Final Thoughts
The advantage of the AliasedTypes is that I can give it any string that I want, and it doesn't matter how much I refactor the code, the communication between the .NET and the JavaScript (or whatever consumer is out there) is unbroken.
Be careful not to mix AliasedTypes and KnownTypes that have the exact same class name, because the code is written that the AliasType will win out over KnownType. When the binder doesn't recognize a type (aliased or known), it will provide the full assembly name of the type.
In the end, I broke down and added the .NET class information to the module in string variables to make refactoring easier.
module.net = {};
module.net.classes = {};
module.net.classes['column'] = "ColumnStructure";
module.net.classes['function'] = "FunctionStructure";
module.net.classes['operand'] = "OperandStructure";
module.net.getAssembly = function (className) {
return "MyNamespace.Models." + module.net.classes[className] + ", MyAssembly";
}
and generated the JSON as
{
"FormulaItem": {
"$type": module.net.getAssembly('column'),
"type": "int",
"field": "my_field",
"display": "My Field"
}
}
protobuf-net uses nested protobuf constructs to support inheritance. However, can it be made to push properties into a flat target class that has the same properties as the inherited "serialized" version?
See the test example below. Needless to say the result of the Flat namespace is null for both properties.
Possible solution: copy data into flat.B first on a property by property basis.
Note: this is not the prefered option.
using System;
namespace hierarchy
{
using ProtoBuf;
[ProtoContract]
public class A
{
[ProtoMember(1)]
public string prop1 { get; set; }
}
[ProtoContract]
public class B : A
{
public B()
{
}
[ProtoMember(1)]
public string prop2 { get; set; }
public override string ToString()
{
return "prop1=" + prop1 + ", prop2=" + prop2;
}
}
}
namespace flat
{
using ProtoBuf;
[ProtoContract]
public class B
{
[ProtoMember(1)]
public string prop1 { get; set; }
[ProtoMember(2)]
public string prop2 { get; set; }
public override string ToString()
{
return "prop1=" + prop1 + ", prop2=" + prop2;
}
}
}
namespace TestProtoSerialization
{
using ProtoBuf;
using System.IO;
public class Test2
{
public void Test()
{
var hb = new hierarchy.B();
hb.prop1 = "prop1";
hb.prop2 = "prop2";
var ms = new MemoryStream();
Serializer.Serialize<hierarchy.B>(ms, hb);
var flatB = Serializer.Deserialize<flat.B>(ms);
Console.WriteLine(hb.ToString()); // <----- Output: prop1=prop1, prop2=prop2
Console.WriteLine(flatB.ToString()); // <----- Output: prop1=, prop2=
}
}
public class Program
{
private static void Main(string[] args)
{
var o2 = new Test2();
o2.Test();
}
}
}
Not directly, and I'm not sure there is a great need to. Maybe I am missing something in the example...
To pick up on the key point - even forgetting about inheritance you've broken the contract - te fields in your exampl are 1 & 1 in one model and 1 & 2 in the other.
It really depends what your objective is; if you just want to push the data over, then sure you can set up a RuntimeTypeModel that only knows about the derived type (disable automatic configuration and add the fields manually). This will then only work for the derived type (obviously), but will output the data as expected by the flat model:
var model = TypeModel.Create();
model.Add(typeof(B), false)
.Add("prop1", "prop2");
Then use model.Serialize etc.
However, writing a flat conversion method on c#, or using AutoMapper would be more obvious. I would only use the above if my objective is to remove the inheritance from the output, for example for interoperability reasons.