Just want to stress out this question is irrelevant of why this technology and why not that. This is related to C# code.
I searched below but none provides solution.
Serialize a class that implements IEnumerable
Serialization on classes that implement IEnumerator
I am working on WCF Rest project in C# which is replacing .NET Remoting communication layer. I use Newtonsoft dll to serialize and de-serialize.
I have a class called MyDTO which implements IEnumerator, IEnumerable which I cannot change since this is old class and many applications in production use them. When I try to serialize MyDTO into string I do not get any error/exception message, I just get an empty array like "[]". Can anybody tell how we can serialize/deserialze class that implements IEnumerator, IEnumerable?
public class MyDTO : IEnumerator, IEnumerable
I am calling a method called ABC in OldServer.dll which gives me MyDTO object. I want to convert this to string and again from string to MyDTO.
Please let me know if you need more information. Please see below for MyDTO class which I cannot change:
[Serializable]
public class MyDTO : IEnumerator, IEnumerable
{
#region Inner Types
internal class MyObjComparer : IComparer<MyObj>
{
public int Compare(MyObj x, MyObj y)
{
return x.InputCode.CompareTo(y.InputCode);
}
}
#endregion
#region Variables
private List<MyObj> myObjList;
private string selectedObjectMessage;
private bool containSequenceNo = false;
private bool sortEnabled;
private bool filterEnabled;
private IComparer<MyObj> objectComparer;
#endregion
#region Constructors
public MyDTO()
{
this.myObjList = new List<MyObj>();
this.selectedObjectMessage = string.Empty;
}
public MyDTO(List<MyObj> objects)
{
this.myObjList = objects;
this.selectedObjectMessage = string.Empty;
}
public MyDTO(IComparer<MyObj> argSortComparer)
: this()
{
this.objectComparer = argSortComparer;
}
public MyDTO(List<MyObj> argErrors, IComparer<MyObj> argSortComparer)
: this(argErrors)
{
this.objectComparer = argSortComparer;
}
public MyDTO(List<MyObj> argErrors, IComparer<MyObj> argSortComparer, bool argSortEnabled)
: this(argErrors, argSortComparer)
{
this.sortEnabled = argSortEnabled;
}
#endregion
#region Properties
public string selectedObjectMessage
{
get { return this.selectedObjectMessage; }
set
{
if (value == null)
value = string.Empty;
this.selectedObjectMessage = value;
}
}
public int Count
{
get { return this.myObjList.Count; }
}
public bool ErrorsContainsSequenceNo
{
get { return this.containSequenceNo; }
set { this.containSequenceNo = value; }
}
public List<MyObj> myObjList
{
get { return this.myObjList; }
set { this.myObjList = value; }
}
public MyDTO WithoutEmptyMyObjects
{
get
{
MyDTO objs = new MyDTO();
foreach (MyObj obj in this.myObjList)
{
if (!string.IsNullOrEmpty(obj.InputCode))
objs.Add(obj);
}
return objs;
}
}
#endregion
}
UPDATE 1:
After spending almost a day we decided to write our own method targeting MyDTO which will produce XmlDocument which is serialize. Using that same XmlDocument we will try create MyDTO object. I feel we need to just concentrate on properties that has setters. I will let you know.
Using the almighty Json.Net
1) Create a contract resolver to serialize/deserialize specific members based on their accessibility, cause by default serialization/deserialization only happens for public members.
public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}
2) Then you can serialize your object like
var settings = new JsonSerializerSettings() { ContractResolver = new MyContractResolver() };
// dtoInstance is your dto instance you wish to serialize.
string serializedDTO = JsonConvert.SerializeObject(dtoInstance, settings);
3) And you can deserialize like
var settings = new JsonSerializerSettings() { ContractResolver = new MyContractResolver() };
MyDTO deserializedDTO = JsonConvert.DeserializeObject<MyDTO>(serializedDTO, settings);
Related
So let's say I have a:
List<IInterface> list;
that has been serialized with TypeNameHandling.Auto, so it has "dynamic" type information. I can deserialize it fine as Newtonsoft.Json can recognize the type from the $type and Json can use the correct constructor. So far so good.
Now say I want to override the creation converter with a mehtod:
CustomCreationConverter<IInterface>
that overrides the creation of the object:
public override IInterface Create(Type objectType)
At this point objectType will always be IInterface and not a derived implementation, so I have no way to create the correct object. The meta-information of $type is now lost.
Is there an elegant way to fix this?
Here would be an attempt that does not work:
public class CustomConverter : CustomCreationConverter<Example.IInterface> {
public override Example.IInterface Create(Type objectType) {
return Example.MakeObject(objectType); // this won't work, objectType will always be IInterface
}
}
public class Example {
public interface IInterface { };
public class A : IInterface { public int content; };
public class B : IInterface { public float data; };
public static IInterface MakeObject(Type t) {
if (t == typeof(IInterface)) {
throw new Exception();
}
return t == typeof(A) ? new A() : new B();
}
public static void Serialize() {
var settings = new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.Auto
};
JsonSerializer serializer = JsonSerializer.Create(settings);
// serializer.Converters.Add(new CustomConverter()); // ?? can't have both, either CustomConverter or $type
List<IInterface> list = new() { MakeObject(typeof(A)), MakeObject(typeof(B)) };
using (StreamWriter sw = new("example.json")) {
serializer.Serialize(sw, list);
}
// Now read back example.json into a List<IInterface> using MakeObject
// Using CustomConverter won't work
using (JsonTextReader rd = new JsonTextReader(new StreamReader("example.json"))) {
List<IInterface> list2 = serializer.Deserialize<List<IInterface>>(rd);
}
}
}
Once you provide a custom converter such as CustomCreationConverter<T> for a type, the converter is responsible for all the deserialization logic including logic for type selection logic that would normally be implemented by TypeNameHandling. If you only want to inject a custom factory creation method and leave all the rest of the deserialization logic unchanged, you could create your own custom contract resolver and inject the factory method as JsonContract.DefaultCreator.
To implement this, first define the following factory interface and contract resolver:
public interface IObjectFactory<out T>
{
bool CanCreate(Type type);
T Create(Type type);
}
public class ObjectFactoryContractResolver : DefaultContractResolver
{
readonly IObjectFactory<object> factory;
public ObjectFactoryContractResolver(IObjectFactory<object> factory) => this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
if (factory.CanCreate(objectType))
{
contract.DefaultCreator = () => factory.Create(objectType);
contract.DefaultCreatorNonPublic = false;
}
return contract;
}
}
Next, refactor your IInterface class hierarchy to make use of an IObjectFactory as an object creation factory:
public class InterfaceFactory : IObjectFactory<IInterface>
{
public InterfaceFactory(string runtimeId) => this.RuntimeId = runtimeId; // Some value to inject into the constructor
string RuntimeId { get; }
public bool CanCreate(Type type) => !type.IsAbstract && typeof(IInterface).IsAssignableFrom(type);
public IInterface Create(Type type) => type switch
{
var t when t == typeof(A) => new A(RuntimeId),
var t when t == typeof(B) => new B(RuntimeId),
_ => throw new NotImplementedException(type.ToString()),
};
}
public interface IInterface
{
public string RuntimeId { get; }
}
public class A : IInterface
{
[JsonIgnore] public string RuntimeId { get; }
internal A(string id) => this.RuntimeId = id;
public int content { get; set; }
}
public class B : IInterface
{
[JsonIgnore] public string RuntimeId { get; }
internal B(string id) => this.RuntimeId = id;
public float data { get; set; }
}
(Here RuntimeId is some value that needs to be injected during object creation.)
Now you will be able to construct your list as follows:
var valueToInject = "some value to inject";
var factory = new InterfaceFactory(valueToInject);
List<IInterface> list = new() { factory.Create(typeof(A)), factory.Create(typeof(B)) };
And serialize and deserialize as follows:
var resolver = new ObjectFactoryContractResolver(factory)
{
// Set any necessary properties e.g.
NamingStrategy = new CamelCaseNamingStrategy(),
};
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
TypeNameHandling = TypeNameHandling.Auto,
};
var json = JsonConvert.SerializeObject(list, Formatting.Indented, settings);
var list2 = JsonConvert.DeserializeObject<List<IInterface>>(json, settings);
Notes:
Newtosoft recommends that you cache and reuse your contract resolvers for best performance.
Newtonsoft also recommends that
TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.
For why, see e.g. TypeNameHandling caution in Newtonsoft Json or External json vulnerable because of Json.Net TypeNameHandling auto?.
Demo fiddle here.
I get the following error with the code below:
"An object reference is required for the non-static field, method, or
property 'Response.PropName'"
Code:
public class Response<T> : Response
{
private string PropName
{
get
{
return typeof(T).Name;
}
}
[JsonProperty(PropName)]
public T Data { get; set; }
}
What you're trying to do is possible, but not trivial, and can't be done with only the built-in attributes from JSON.NET. You'll need a custom attribute, and a custom contract resolver.
Here's the solution I came up with:
Declare this custom attribute:
[AttributeUsage(AttributeTargets.Property)]
class JsonPropertyGenericTypeNameAttribute : Attribute
{
public int TypeParameterPosition { get; }
public JsonPropertyGenericTypeNameAttribute(int position)
{
TypeParameterPosition = position;
}
}
Apply it to your Data property
public class Response<T> : Response
{
[JsonPropertyGenericTypeName(0)]
public T Data { get; set; }
}
(0 is the position of T in Response<T>'s generic type parameters)
Declare the following contract resolver, which will look for the JsonPropertyGenericTypeName attribute and get the actual name of the type argument:
class GenericTypeNameContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
var attr = member.GetCustomAttribute<JsonPropertyGenericTypeNameAttribute>();
if (attr != null)
{
var type = member.DeclaringType;
if (!type.IsGenericType)
throw new InvalidOperationException($"{type} is not a generic type");
if (type.IsGenericTypeDefinition)
throw new InvalidOperationException($"{type} is a generic type definition, it must be a constructed generic type");
var typeArgs = type.GetGenericArguments();
if (attr.TypeParameterPosition >= typeArgs.Length)
throw new ArgumentException($"Can't get type argument at position {attr.TypeParameterPosition}; {type} has only {typeArgs.Length} type arguments");
prop.PropertyName = typeArgs[attr.TypeParameterPosition].Name;
}
return prop;
}
}
Serialize with this resolver in your serialization settings:
var settings = new JsonSerializerSettings { ContractResolver = new GenericTypeNameContractResolver() };
string json = JsonConvert.SerializeObject(response, settings);
This will give the following output for Response<Foo>
{
"Foo": {
"Id": 0,
"Name": null
}
}
Here's a potentially easier way to achieve it. All you need to do is to have Response extend JObject, like this:
public class Response<T>: Newtonsoft.Json.Linq.JObject
{
private static string TypeName = (typeof(T)).Name;
private T _data;
public T Data {
get { return _data; }
set {
_data = value;
this[TypeName] = Newtonsoft.Json.Linq.JToken.FromObject(_data);
}
}
}
If you do that, the following would work as you expect:
static void Main(string[] args)
{
var p1 = new Response<Int32>();
p1.Data = 5;
var p2 = new Response<string>();
p2.Data = "Message";
Console.Out.WriteLine("First: " + JsonConvert.SerializeObject(p1));
Console.Out.WriteLine("Second: " + JsonConvert.SerializeObject(p2));
}
Output:
First: {"Int32":5}
Second: {"String":"Message"}
In case you can't have Response<T> extend JObject, because you really need it to extend Response, you could have Response itself extend JObject, and then have Response<T> extend Response as before. It should work just the same.
#Thomas Levesque: OK. So let's say that you can't extend JObject in Response<T> because you need to extend a pre-existing Response class. Here's another way you could implement the same solution:
public class Payload<T> : Newtonsoft.Json.Linq.JObject {
private static string TypeName = (typeof(T)).Name;
private T _data;
public T Data {
get { return _data; }
set {
_data = value;
this[TypeName] = Newtonsoft.Json.Linq.JToken.FromObject(_data);
}
}
}
//Response is a pre-existing class...
public class Response<T>: Response {
private Payload<T> Value;
public Response(T arg) {
Value = new Payload<T>() { Data = arg };
}
public static implicit operator JObject(Response<T> arg) {
return arg.Value;
}
public string Serialize() {
return Value.ToString();
}
}
So now there are the following options to Serialize the class:
static void Main(string[] args) {
var p1 = new Response<Int32>(5);
var p2 = new Response<string>("Message");
JObject p3 = new Response<double>(0.0);
var p4 = (JObject) new Response<DateTime>(DateTime.Now);
Console.Out.WriteLine(p1.Serialize());
Console.Out.WriteLine(p2.Serialize());
Console.Out.WriteLine(JsonConvert.SerializeObject(p3));
Console.Out.WriteLine(JsonConvert.SerializeObject(p4));
}
The Output will look something like this:
{"Int32":5}
{"String":"Message"}
{"Double":0.0}
{"DateTime":"2016-08-25T00:18:31.4882199-04:00"}
Using a C# class generated from an XSD document, I can create an object, and serialize it successfully. However, some attributes have an XmlDefaultValue defined. If any objects have the default value, then those attributes do not get created when the object is serialized.
This is expected behavior according to the documentation. But this is not how I want it to behave. I need to have all such attributes generated in the XML document.
I've checked for any code attributes that can be applied that might force it to be outputted, even if it is the default value, but I couldn't find anything like that.
Is there any way to make that work?
The last answer regarding DataContract is NOT the answer. The XSD is generated automatically and the person consuming the classes is not in control of the attributes used by the original author. The question was about auto-generated classes based on an XSD.
The other answer is problematic too because properties that have defaults defined also may not allow null values (this happens often). The only real solution is to have a serializer where you can tell it what properties to ignore with respect to serialization. This has been and always be a serious problem with current XML serializers that simply don't allow one to pass in what properties to force being serialized.
Actual scenario:
A REST service accepts XML in the body to update an object. The XML has an XSD defined by the author of the rest service. The current object stored by the rest service has a non-default value set. The users modifies the XML to change it back to the default... but the serialized version put in the body of the REST post skips the value and doesn't include it because its set to a default value.
What a quagmire... can't update the value because the logic behind not exporting default values completely ignores the idea that XML can be used to update an object, not just create new ones based on the XML. I can't believe its been this many years and nobody modified XML serializers to handle this basic scenario with ease.
You can do this for a specific set of types when serializing by constructing an XmlAttributeOverrides that specifies new XmlAttributes() { XmlDefaultValue = null } for every field or property that has DefaultValueAttribute applied, then passing this to the XmlSerializer(Type, XmlAttributeOverrides) constructor:
var overrides = new XmlAttributeOverrides();
var attrs = new XmlAttributes() { XmlDefaultValue = null };
overrides.Add(typeToSerialize, propertyNameWithDefaultToIgnore, attrs);
var serializer = new XmlSerializer(typeToSerialize, overrides);
Note, however, this important warning from the documentation:
Dynamically Generated Assemblies
To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors:
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)
If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies in a Hashtable, as shown in the following example.
However, the example given in the code doesn't give any suggestion of how to key the hashtable. It also isn't thread-safe. (Perhaps it dates from .Net 1.0?)
The following code creates a key scheme for xml serializers with overrides, and manufactures (via reflection) serializers for which the [DefaultValue] values (if any) of all properties and fields are overridden to be null, effectively cancelling the default value. Note, when creating a blank XmlAttributes() object all attributes are set to null. When overriding with this XmlAttributes object any attributes that are desired to stay need to be transferred into this new object:
public abstract class XmlSerializerKey
{
static class XmlSerializerHashTable
{
static Dictionary<object, XmlSerializer> dict;
static XmlSerializerHashTable()
{
dict = new Dictionary<object, XmlSerializer>();
}
public static XmlSerializer GetSerializer(XmlSerializerKey key)
{
lock (dict)
{
XmlSerializer value;
if (!dict.TryGetValue(key, out value))
dict[key] = value = key.CreateSerializer();
return value;
}
}
}
readonly Type serializedType;
protected XmlSerializerKey(Type serializedType)
{
this.serializedType = serializedType;
}
public Type SerializedType { get { return serializedType; } }
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
else if (ReferenceEquals(null, obj))
return false;
if (GetType() != obj.GetType())
return false;
XmlSerializerKey other = (XmlSerializerKey)obj;
if (other.serializedType != serializedType)
return false;
return true;
}
public override int GetHashCode()
{
int code = 0;
if (serializedType != null)
code ^= serializedType.GetHashCode();
return code;
}
public override string ToString()
{
return string.Format(base.ToString() + ": for type: " + serializedType.ToString());
}
public XmlSerializer GetSerializer()
{
return XmlSerializerHashTable.GetSerializer(this);
}
protected abstract XmlSerializer CreateSerializer();
}
public abstract class XmlserializerWithExtraTypesKey : XmlSerializerKey
{
static IEqualityComparer<HashSet<Type>> comparer;
readonly HashSet<Type> extraTypes = new HashSet<Type>();
static XmlserializerWithExtraTypesKey()
{
comparer = HashSet<Type>.CreateSetComparer();
}
protected XmlserializerWithExtraTypesKey(Type serializedType, IEnumerable<Type> extraTypes)
: base(serializedType)
{
if (extraTypes != null)
foreach (var type in extraTypes)
this.extraTypes.Add(type);
}
public Type[] ExtraTypes { get { return extraTypes.ToArray(); } }
public override bool Equals(object obj)
{
if (!base.Equals(obj))
return false;
XmlserializerWithExtraTypesKey other = (XmlserializerWithExtraTypesKey)obj;
return comparer.Equals(this.extraTypes, other.extraTypes);
}
public override int GetHashCode()
{
int code = base.GetHashCode();
if (extraTypes != null)
code ^= comparer.GetHashCode(extraTypes);
return code;
}
}
public sealed class XmlSerializerIgnoringDefaultValuesKey : XmlserializerWithExtraTypesKey
{
readonly XmlAttributeOverrides overrides;
private XmlSerializerIgnoringDefaultValuesKey(Type serializerType, IEnumerable<Type> ignoreDefaultTypes, XmlAttributeOverrides overrides)
: base(serializerType, ignoreDefaultTypes)
{
this.overrides = overrides;
}
public static XmlSerializerIgnoringDefaultValuesKey Create(Type serializerType, IEnumerable<Type> ignoreDefaultTypes, bool recurse)
{
XmlAttributeOverrides overrides;
Type [] typesWithOverrides;
CreateOverrideAttributes(ignoreDefaultTypes, recurse, out overrides, out typesWithOverrides);
return new XmlSerializerIgnoringDefaultValuesKey(serializerType, typesWithOverrides, overrides);
}
protected override XmlSerializer CreateSerializer()
{
var types = ExtraTypes;
if (types == null || types.Length < 1)
return new XmlSerializer(SerializedType);
return new XmlSerializer(SerializedType, overrides);
}
static void CreateOverrideAttributes(IEnumerable<Type> types, bool recurse, out XmlAttributeOverrides overrides, out Type[] typesWithOverrides)
{
HashSet<Type> visited = new HashSet<Type>();
HashSet<Type> withOverrides = new HashSet<Type>();
overrides = new XmlAttributeOverrides();
foreach (var type in types)
{
CreateOverrideAttributes(type, recurse, overrides, visited, withOverrides);
}
typesWithOverrides = withOverrides.ToArray();
}
static void CreateOverrideAttributes(Type type, bool recurse, XmlAttributeOverrides overrides, HashSet<Type> visited, HashSet<Type> withOverrides)
{
if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string) || visited.Contains(type))
return;
var attrs = new XmlAttributes() { XmlDefaultValue = null };
foreach (var property in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
if (overrides[type, property.Name] == null) // Check to see if overrides for this base type were already set.
if (property.GetCustomAttributes<DefaultValueAttribute>(true).Any())
{
withOverrides.Add(type);
overrides.Add(type, property.Name, attrs);
}
foreach (var field in type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
if (overrides[type, field.Name] == null) // Check to see if overrides for this base type were already set.
if (field.GetCustomAttributes<DefaultValueAttribute>(true).Any())
{
withOverrides.Add(type);
overrides.Add(type, field.Name, attrs);
}
visited.Add(type);
if (recurse)
{
var baseType = type.BaseType;
if (baseType != type)
CreateOverrideAttributes(baseType, recurse, overrides, visited, withOverrides);
}
}
}
And then you would call it like:
var serializer = XmlSerializerIgnoringDefaultValuesKey.Create(typeof(ClassToSerialize), new[] { typeof(ClassToSerialize), typeof(AdditionalClass1), typeof(AdditionalClass2), ... }, true).GetSerializer();
For example, in the following class hierarchy:
public class BaseClass
{
public BaseClass() { Index = 1; }
[DefaultValue(1)]
public int Index { get; set; }
}
public class MidClass : BaseClass
{
public MidClass() : base() { MidDouble = 1.0; }
[DefaultValue(1.0)]
public double MidDouble { get; set; }
}
public class DerivedClass : MidClass
{
public DerivedClass() : base() { DerivedString = string.Empty; }
[DefaultValue("")]
public string DerivedString { get; set; }
}
public class VeryDerivedClass : DerivedClass
{
public VeryDerivedClass() : base() { this.VeryDerivedIndex = -1; }
[DefaultValue(-1)]
public int VeryDerivedIndex { get; set; }
}
The default XmlSerializer produces:
<VeryDerivedClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
But the custom serializer produces
<?xml version="1.0" encoding="utf-16"?>
<VeryDerivedClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Index>1</Index>
<MidDouble>1</MidDouble>
<DerivedString />
<VeryDerivedIndex>-1</VeryDerivedIndex>
</VeryDerivedClass>
Finally, note that writing of null values is controlled by [XmlElement( IsNullable = true )] so writing of nulls is not affected by this serializer.
Example how to force serialize all public properties with XmlDefaultValue attribute:
[Test]
public void GenerateXMLWrapTest()
{
var xmlWrap = new XmlWrap();
using (var sw = new StringWriter())
{
var overrides = new XmlAttributeOverrides();
var attrs = new XmlAttributes { XmlDefaultValue = null };
var type = typeof(XmlWrap);
foreach (var propertyInfo in type.GetProperties())
{
if (propertyInfo.CanRead && propertyInfo.CanWrite && propertyInfo.GetCustomAttributes(true).Any(o => o is DefaultValueAttribute))
{
var propertyNameWithDefaultToIgnore = propertyInfo.Name;
overrides.Add(type, propertyNameWithDefaultToIgnore, attrs);
}
}
var serializer = new XmlSerializer(type, overrides);
serializer.Serialize(sw, xmlWrap);
sw.Flush();
var xmlString = sw.ToString();
Console.WriteLine(xmlString);
}
}
Output:
<?xml version="1.0" encoding="utf-16"?>
<ConIdTranslator xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:devices-description-1.0">
<Disabled>false</Disabled>
<HostPortParams>COM1 baud=115200 parity=None data=8 stop=One</HostPortParams>
<TranslatorObjectNumber>9000</TranslatorObjectNumber>
...
Where Disabled, HostPortParams, TranslatorObjectNumber public properties of serialized class has default value attribute:
[Serializable]
[XmlRoot("ConIdTranslator", Namespace = "urn:devices-description-1.0", IsNullable = false)]
public class ConIdTranslatorXmlWrap : HardwareEntityXmlWrap
{
#region Fields
[EditorBrowsable(EditorBrowsableState.Never)]
[XmlIgnore]
private string hostPortParams = "COM1 baud=115200 parity=None data=8 stop=One";
[EditorBrowsable(EditorBrowsableState.Never)]
[XmlIgnore]
private bool disabled = false;
...
#endregion
#region Properties
[XmlElement]
[DefaultValue(false)]
public bool Disabled
{
get => this.disabled;
set
{
this.disabled = value;
this.OnPropertyChanged("Disabled");
}
}
[XmlElement]
[DefaultValue("COM1 baud=115200 parity=None data=8 stop=One")]
public string HostPortParams
{
get => this.hostPortParams;
set
{
this.hostPortParams = value;
this.OnPropertyChanged("HostPortParams");
}
}
...
I found the answer:
https://msdn.microsoft.com/en-us/library/system.runtime.serialization.datamemberattribute.emitdefaultvalue%28v=vs.110%29.aspx
Set the attribute in the DataContract like this: [DataMember(EmitDefaultValue=true)]
I have a java library that contains all the domain models for our backend REST API. The backend API is implemented in Java and translate Java objects to JSON using Jackson.
Recently we need to implement a new feature and have a Windows .NET application talk to our API.
However, since the domain model (contract) is all in Java, we had to translate all the Java classes to C# classes so that we can use Json.NET to serialize/deserialize JSON, but this quickly became time-consuming. In addition, whenver there is contract change in Java we likely have to do this for C# classes also.
I searched online and found out IKVMC can translate a jar to a DLL, so I tried it out, however, it's causing some Json.NET serialization problems.
For example
I have a Java object that looks like this:
public class ApiToken {
private String apiKey;
private String apiSecret;
public String getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
public String getApiSecret() {
return apiSecret;
}
public void setApiSecret(String apiSecret) {
this.apiSecret = apiSecret;
}
#Override
public int hashCode() {
return Objects.hashCode(apiKey, apiSecret);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ApiToken other = (ApiToken) obj;
return Objects.equal(this.apiKey, other.apiKey) && Objects.equal(this.apiSecret, other.apiSecret);
}
#Override
public String toString() {
return Objects.toStringHelper(this).add("apiKey", apiKey).add("apiSecret", apiSecret).toString();
}
}
AFTER translation by ikvmc, it looks like this:
public class ApiToken : Object
{
[LineNumberTable(11)]
[MethodImpl(MethodImplOptions.NoInlining)]
public ApiToken();
public virtual string getApiKey();
public virtual void setApiKey(string apiKey);
public virtual string getApiSecret();
public virtual void setApiSecret(string apiSecret);
[LineNumberTable(35)]
public override int hashCode();
[LineNumberTable(new byte[] {159, 182, 100, 98, 99, 98, 110, 98, 103})]
[MethodImpl(MethodImplOptions.NoInlining)]
public override bool equals(object obj);
[LineNumberTable(52)]
public override string toString();
}
when I create this object in C#, JSON.net does NOT seralize this object correctly. Instead, it just produces an empty JSON {}
I suspect this is because there are no fields/properties exposed in the object that is generated by ikvmc.
Does anyone know if there's a workaround for this?
Thanks a lot and much appreciated
Update:
This is how I'm serializing the object
ApiToken apiToken = new ApiToken();
apiToken.setApiKey("test");
apiToken.setApiSecret("secret");
string json = JsonConvert.SerializeObject(apiToken);
The json output is {}.
I suspect this is because there are no fields/properties exposed in the object that is generated by ikvmc
Yes.
Does anyone know if there's a workaround for this?
You can do it by writing a custom ContractResolver and ValueProvider as below
var obj = new ApiToken();
obj.setApiKey("X-X-X");
obj.setI(666);
var settings = new Newtonsoft.Json.JsonSerializerSettings() {
ContractResolver = new MyContractResolver()
};
var json = JsonConvert.SerializeObject(obj, settings);
//json : {"ApiKey":"X-X-X","I":666}
var newobj = JsonConvert.DeserializeObject<ApiToken>(json, settings);
//Test class
public class ApiToken
{
private String apiKey;
public String getApiKey()
{
return apiKey;
}
public void setApiKey(String apiKey)
{
this.apiKey = apiKey;
}
private int i;
public int getI()
{
return i;
}
public void setI(int i)
{
this.i = i;
}
public string dummy()
{
return "abcde";
}
}
public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
{
//Find methods. setXXX getXXX
var properties = type.GetMethods()
.Where(m => m.Name.Length > 3)
.GroupBy(m => m.Name.Substring(3))
.Where(g => g.Count() == 2 && g.Any(x=>x.Name=="set" + g.Key) && g.Any(x=>x.Name=="get" + g.Key))
.ToList();
//Create a JsonProperty for each set/getXXX pair
var ret = properties.Select(prop=>
{
var jProp = new Newtonsoft.Json.Serialization.JsonProperty();
jProp.PropertyName = prop.Key;
jProp.PropertyType = prop.First(m => m.Name.StartsWith("get")).ReturnType;
jProp.ValueProvider = new MyValueProvider(prop.ToList());
jProp.Readable = jProp.Writable = true;
return jProp;
})
.ToList();
return ret;
}
}
public class MyValueProvider : Newtonsoft.Json.Serialization.IValueProvider
{
List<MethodInfo> _MethodInfos = null;
public MyValueProvider(List<MethodInfo> methodInfos)
{
_MethodInfos = methodInfos;
}
public object GetValue(object target)
{
return _MethodInfos.First(m => m.Name.StartsWith("get")).Invoke(target, null);
}
public void SetValue(object target, object value)
{
_MethodInfos.First(m => m.Name.StartsWith("set")).Invoke(target, new object[] { value });
}
}
I wrote a class that allows a derivate to specify which of its properties can be lazy loaded. The code is:
public abstract class SelfHydratingEntity<T> : DynamicObject where T : class {
private readonly Dictionary<string, LoadableBackingField> fields;
public SelfHydratingEntity(T original) {
this.Original = original;
this.fields = this.GetBackingFields().ToDictionary(f => f.Name);
}
public T Original { get; private set; }
protected virtual IEnumerable<LoadableBackingField> GetBackingFields() {
yield break;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
result = field.GetValue();
return true;
} else {
var getter = PropertyAccessor.GetGetter(this.Original.GetType(), binder.Name);
result = getter(this.Original);
return true;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
field.SetValue(value);
return true;
} else {
var setter = PropertyAccessor.GetSetter(this.Original.GetType(), binder.Name);
setter(this.Original, value);
return true;
}
}
}
And a derivate class:
public class SelfHydratingPerson : SelfHydratingEntity<IPerson> {
private readonly IDataRepository dataRepository;
public SelfHydratingDerivate(IDataRepository dataRepository, IPerson person)
: base(person) {
this.dataRepository = dataRepository
}
protected override IEnumerable<LoadableBackingField> GetBackingFields() {
yield return new LoadableBackingField("Address", () => this.dataRepository.Addresses.Get(this.Original.AddressID));
}
}
This works perfectly fine for getting and settings property values, but I get a either a RuntimeBinderException when I implicitly cast or an InvalidCastException with an explicitly cast SelfHydratingEntity back to T.
I know that you can override the DynamicObject.TryConvert method, but I'm wondering what exactly to put in this method. I've read a lot about duck typing today, and have tried out several libraries, but none of them work for this particular scenario. All of the libraries I've tried today generate a wrapper class using Reflection.Emit that makes calls to "get_" and "set_" methods and naturally use reflection to find these methods on the wrapped instance. SelfHydratingEntity of course doesn't have the "get_" and "set_" methods defined.
So, I'm wondering if this kind of thing is even possible. Is there any way to cast an instance of SelfHydratingEntity to T? I'm looking for something like this:
var original = GetOriginalPerson();
dynamic person = new SelfHydratingPerson(new DataRepository(), original);
string name = person.Name; // Gets property value on original
var address = person.Address; // Gets property value using LoadableBackingField registration
var iPerson = (IPerson)person;
- or -
var iPerson = DuckType.As<IPerson>(person);
Have you seen this Duck Typing project. It looks pretty good. I have just found a great example from Mauricio. It uses the Windsor Castle dynamic proxy to intercept method calls
Using the code from Mauricio the following code works like a dream
class Program
{
static void Main(string[] args)
{
dynamic person = new { Name = "Peter" };
var p = DuckType.As<IPerson>(person);
Console.WriteLine(p.Name);
}
}
public interface IPerson
{
string Name { get; set; }
}
public static class DuckType
{
private static readonly ProxyGenerator generator = new ProxyGenerator();
public static T As<T>(object o)
{
return generator.CreateInterfaceProxyWithoutTarget<T>(new DuckTypingInterceptor(o));
}
}
public class DuckTypingInterceptor : IInterceptor
{
private readonly object target;
public DuckTypingInterceptor(object target)
{
this.target = target;
}
public void Intercept(IInvocation invocation)
{
var methods = target.GetType().GetMethods()
.Where(m => m.Name == invocation.Method.Name)
.Where(m => m.GetParameters().Length == invocation.Arguments.Length)
.ToList();
if (methods.Count > 1)
throw new ApplicationException(string.Format("Ambiguous method match for '{0}'", invocation.Method.Name));
if (methods.Count == 0)
throw new ApplicationException(string.Format("No method '{0}' found", invocation.Method.Name));
var method = methods[0];
if (invocation.GenericArguments != null && invocation.GenericArguments.Length > 0)
method = method.MakeGenericMethod(invocation.GenericArguments);
invocation.ReturnValue = method.Invoke(target, invocation.Arguments);
}
}
impromptu-interface
https://github.com/ekonbenefits/impromptu-interface
Can static cast interfaces onto objects derived from DynamicObject.