c# XmlSerialization collapse class to give particular value - c#

I'm working on some classes in c#. They need to be serialized to XML to match an existing schema, but I want to build the validation in the code. e.g. something like the below;
public class Address {
public AddressLineType AddressLine1 {get;set;}
public AddressLineType AddressLine2 {get;set;}
public ShortAddressLineType AddressTown {get;set;}
public ShortAddressLineType AddressCounty {get;set;}
public PostCodeType PostCode {get;set;}
}
public class SimpleString {
public string Value {get;set;}
public override string ToString() {
return Value;
}
}
public class AddressLineType : SimpleString {
static readonly int maxLength = 60;
public static explicit operator AddressLineType(string v) {
if(!(v.Length < maxLength)) throw new Exception("String is to long!");
AddressLineType s = new AddressLineType();
s.Value = v;
return s;
}
}
public class ShortAddressLineType : SimpleString {
static readonly int maxLength = 30;
public static explicit operator ShortAddressLineType(string v) {
if(!(v.Length < maxLength)) throw new Exception("String is to long!");
ShortAddressLineType s = new ShortAddressLineType();
s.Value = v;
return s;
}
}
public class PostCodeType : SimpleString {
public static explicit operator PostCodeType(string v) {
Regex regex = new Regex(""); //PostCode pattern..
if(!(regex.IsMatch(v))) throw new Exception("Regex Validation Failed.");
PostCodeType s = new PostCodeType();
s.Value = v;
return s;
}
}
However, when this is serialized the result is;
<CurrentAddress>
<AddressLine1>
<Value>3 The Street</Value>
</AddressLine1>
<AddressLine2>
<Value>Foo Town</Value>
</AddressLine2>
</CurrentAddress>
Is it possible to collapse the special classes so that the result is this instead, but still be able to do validation? e.g. I need to specify that the AddressLineType serialises to it's Value only.
<CurrentAddress>
<AddressLine1>3 The Street</AddressLine1>
<AddressLine2>Foo Town</AddressLine2>
</CurrentAddress>
This code prints the result;
Address address = new Address();
address.AddressLine1 = (AddressLineType) "3 The Street";
address.AddressLine2 = (AddressLineType) "Foo Town";
XmlSerializer serializer = new XmlSerializer(typeof(Address));
serializer.Serialize(Console.Out, address);

Related

Dynamic cast in c# in runtime

I have 2 classes as you can see :
static void Main(string[] args)
{
object m = (??????)"salam";
}
public class A
{
public string name { set; get; }
public static implicit operator A(string _name)
{
A aa = new A();
aa.name = _name;
return aa;
}
}
public class B
{
public string family { set; get; }
public static implicit operator B(string _family)
{
B bb = new B();
bb.family = _family;
return bb;
}
}
I need to cast my string in runtime in this line :
object m = (??????)"salam";
Is there any solution to pass my class name as a string to cast my value .for example in runtime I need to cast "salam" to A or maybe B
The static cast is working good like this
object m = (A)salam";
object m = (B)"salam";
But I need to cast my string in runtime
Type x=null;
If(condition)
x can be type of A
else
x can be type of B
object m = (x)"salam";
You need to use Interfaces for such a need. The following code shows how to do so.
To simulate your situtation, I wrote a method to return either A or B based on time.
Here the list contains a bunch of objects which may be of Type A or B, depending on the second of execution. In the real-world scenario, you would get your types in various other ways.
public class StackOverflowQuestion
{
public static void Run()
{
List<IBase> list = new List<IBase>();
string types = "";
for (int i = 0; i < 10; i++)
{
var randomType = GiveMeARandomIBaseType();
System.Threading.Thread.Sleep(750);
IBase hello = randomType.Convert("salam");
list.Add(hello);
types += hello.GetType().Name + ",";
}
types = types.Trim(',');
//sample result : B,B,A,B,A,A,B,A,B,B
}
static IBase GiveMeARandomIBaseType() {
if (DateTime.Now.Second % 2 == 0)
return new A();
else
return new B();
}
}
public interface IBase {
public IBase Convert(string s);
}
public static class MyExtensions {
public static T Convert<T>(this string str, IBase b) where T : IBase {
try
{
return (T)b.Convert(str);
}
catch (Exception)
{
return default;
}
}
}
public class A : IBase
{
public IBase Convert(string s) {
return (A)s;
}
public string name { set; get; }
public static implicit operator A(string _name)
{
A aa = new A();
aa.name = _name;
return aa;
}
}
public class B : IBase
{
public IBase Convert(string s)
{
return (B)s;
}
public string family { set; get; }
public static implicit operator B(string _family)
{
B bb = new B();
bb.family = _family;
return bb;
}
}
I had a similar problem and after all the study and time, I was able to approach the desired result in the following way.
I used an internal method to access (the inside of) the class and this method returns the cast desired result.
Step 1: in class
public class A
{
public string Name { set; get; }
public static implicit operator A(string name)
{
return new A
{
Name = name
};
}
public A GetCasting(object a)
{
A i = (A)a;
return i;
}
}
public class B
{
public string Family { set; get; }
public static implicit operator B(string family)
{
return new B
{
Family = family
};
}
public B GetCasting(object b)
{
B i = (B)b;
return i;
}
}
Step 2: in controller or code
var className = "A";
var classMethod = "GetCasting";
var classType = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && t.Name == className).FirstOrDefault();
var classInstance = Activator.CreateInstance(classType);
var castMethod = classType.GetMethod(classMethod);
var yourObject = "salam";
var objectData = new object[] { yourObject };
var resultObject = castMethod.Invoke(classInstance, objectData);

Invoke method similar to WCF invoker?

Some strong typed classes:
public class A
{
public C Process(B b)
{
return new C()
{
Output = b.Property + " bar!"
};
}
}
public class B
{
public string Property {get;set;}
}
public class C
{
public string Output {get;set;}
}
Input as JSON string:
string input = "{\"Property\":\"foo\"}";
And what I want is if I have instance of A as object, I want to invoke this JSON and retrieve object output:
object instanceOfA = new A();
object result = instanceOfA.GetType().GetMethod("Process").Invoke(instanceOfA, new []{ /*json here?*/});
So, it is basically similar to some JavaScript call on some plain argument.
What about changing the method signature for the 'Process' method to take a string as input instead and attempting to de-serialize the input to the type you want? So something like this:
string input = "{\"Property\":\"foo\"}";
object instanceOfA = new A();
object result = instanceOfA.GetType().GetMethod("Process").Invoke(instanceOfA, new object[]{ input });
public class A
{
public C Process(string input)
{
var b = (B) JsonConvert.DeserializeObject(input, typeof(B));
if (!string.IsNullOrEmpty(b.Property))
{
return new C()
{
Output = b.Property + " bar!"
};
}
return null;
}
}
public class B
{
public string Property { get; set; }
}
public class C
{
public string Output { get; set; }
}
Problem was simply solved with Newtonsoft.Json features:
public class JsonInvoker
{
private readonly MethodInfo _methodInfo;
private readonly Type _paramType;
private readonly JsonSerializerSettings _settings;
public JsonInvoker(Type instanceType, string methodName)
{
_methodInfo = instanceType.GetMethod(methodName);
_paramType = _methodInfo.GetParameters()[0].ParameterType;
_settings = new JsonSerializerSettings
{
ContractResolver = new RequireObjectPropertiesContractResolver(),
MissingMemberHandling = MissingMemberHandling.Error
};
}
public object Invoke(object instance, string json)
{
var input = JsonConvert.DeserializeObject(json, _paramType, _settings);
var output = _methodInfo.Invoke(instance, new[] { input });
return output;
}
private class RequireObjectPropertiesContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
contract.ItemRequired = Required.AllowNull;
return contract;
}
}
}
Now I can invoke my method:
class Program
{
static void Main(string[] args)
{
object plugin = new A();
string json = "{\"Property\":\"foo\"}";
var invoker = new JsonInvoker(plugin.GetType(), "Process");
//here I call invoker with simple string.
var output = invoker.Invoke(plugin, json);
}
}
public class A
{
public C Process(B input)
{
return new C
{
Property = input.Property + " bar"
};
}
}
public class C
{
public string Property { get; set; }
}
public class B
{
public string Property { get; set; }
}

passing around values to an AutoMapper Type Converter from outside

I have a multilingual database, which returns values based on a key and an enum Language. When I convert a DB object to a model, I want the model to contain the translated value based on the key and the current language.
The key comes from the DB object but how can I pass the current language to the the Mapper.Map() function?
Currently, I am using a [ThreadStatic] attribute to set the culture before calling Mapper.Map<>, and to retrieve it in the TypeConverter.
public enum Language
{
English, French, Italian, Maltese
}
public class MultilingualValue<T>
{
public Dictionary<Language, T> Value { get; set; }
public MultilingualValue()
{
this.Value = new Dictionary<Language, T>();
}
}
public class PersonData
{
public string FirstName { get; set; }
public MultilingualValue<string> City { get; set; }
}
public void MapPerson()
{
PersonData personData = new PersonData();
personData.FirstName = "John";
personData.City = new MultilingualValue<string>();
personData.City.Value[ Language.English] = "The Zurrieq";
personData.City.Value[Language.French] = "Le Zurrieque";
MultilingualValueData.CurrentLanguage = Language.English;
var personModel = Mapper.Map<PersonData, PersonModel>(personData);
}
public class MultilingualValueToBasicDataTypeConverter<T> : ITypeConverter<MultilingualValue<T>, T>
{
public T Convert(ResolutionContext context)
{
var currentLanguage = MultilingualValueData.CurrentLanguage; //THIS IS THE [ThreadStatic] VARIABLE
if (currentLanguage == null) throw new InvalidOperationException("Please make sure to fill in CurrentLanguage");
MultilingualValue<T> sourceMultilingualValue = (MultilingualValue < T > )context.SourceValue;
T destinationValue = default(T);
if (sourceMultilingualValue != null)
{
destinationValue = sourceMultilingualValue.Value[currentLanguage.Value];
}
return destinationValue;
}
}
public static class MultilingualValueData
{
[ThreadStatic]
public static Language? CurrentLanguage;
}
I left out the configurations as I think they're unneccessary for this example. If you need them, I'll post them as well.
While this works, I find this workaround quite ugly. Is there any way to pass data through the ResolutionContext?
Just use the Map overload that takes a Action<IMappingOperationOptions>. You can add configuration elements to the Items property that are then passed to your ITypeConverter
public class CustomConverter : ITypeConverter<string, string>
{
public string Convert(ResolutionContext context)
{
return "translated in " + context.Options.Items["language"];
}
}
internal class Program
{
private static void Main(string[] args)
{
AutoMapper.Mapper.CreateMap<string, string>().ConvertUsing<CustomConverter>();
var result = AutoMapper.Mapper.Map<string, string>("value" , opt => opt.Items["language"] = "english");
Console.Write(result); // prints "translated in english"
Console.ReadLine();
}
}

Linq sorting of entity by custom property via reflection

Got Customer class which has Country property which has string property Name.
Also Customer implements IComparable<Country> like so:
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
Now:
var custList = new List<Customer>{...};
custList.OrderBy(cust => cust.Country).ToList(); //Sorts as charm.
And if try sorting via reflection:
var itemProp = typeof(Customer).GetProperty("Country");
custList = c.Customers.ToList()
.OrderBy(cust => itemProp.GetValue(cust, null)).ToList(); // Fails
Throws exception 'At least one object must implement IComparable'
Please explain why does it fail and how correctly implement sorting of Customer by custom property via reflection. Thanks.
Since GetValue returns Object you need to implement the non generic version of IComparable.
void Main()
{
var custList = new List<Customer>()
{
new Customer(){ Country = new Country(){ Name = "Sweden" } },
new Customer(){ Country = new Country(){ Name = "Denmark" } },
};
var itemProp = typeof(Customer).GetProperty("Country");
custList = custList.OrderBy(cust => itemProp.GetValue(cust, null)).ToList();
custList.Dump();
}
public class Country : IComparable<Country>, IComparable
{
public string Name {get;set;}
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
public int CompareTo(object other)
{
var o = other as Country;
if(o == null)
return 0; //Or how you want to handle it
return CompareTo(o);
}
}
public class Customer
{
public Country Country{get;set;}
}
Assuming that the underlying type is correct (i.e. Country), you should be able to do it as long as Country implements IComparable:
Here's a sample console app that works correctly (note that there is no error handling):
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Number: IComparable<Number>, IComparable
{
public Number(int value)
{
Value = value;
}
public readonly int Value;
public int CompareTo(Number other)
{
return Value.CompareTo(other.Value);
}
public int CompareTo(object obj)
{
return CompareTo((Number) obj);
}
}
class Test
{
public Number Number;
public object Obj
{
get { return Number; }
}
public override string ToString()
{
return Number.Value.ToString();
}
}
internal static class Program
{
static void Main()
{
var itemProp = typeof(Test).GetProperty("Obj");
Console.WriteLine(string.Join("\n",
data().OrderBy(x => itemProp.GetValue(x, null))));
}
static IEnumerable<Test> data()
{
for (int i = 0; i < 10; ++i)
yield return new Test {Number = new Number(10-i)};
}
}
}

XML Serialize List of generic objects derived from an interface,,,

So I'm trying to XML serialize a List<IObject> derrived from an interface, but the IObjects are generic types... best resort to code:
public interface IOSCMethod
{
string Name { get; }
object Value { get; set; }
Type Type { get; }
}
public class OSCMethod<T> : IOSCMethod
{
public string Name { get; set; }
public T Value { get; set; }
public Type Type { get { return _type; } }
protected string _name;
protected Type _type;
public OSCMethod() { }
// Explicit implementation of IFormField.Value
object IOSCMethod.Value
{
get { return this.Value; }
set { this.Value = (T)value; }
}
}
And I have a List of IOSCMethods:
List<IOSCMethod>
of which I add objects to in the following way:
List<IOSCMethod> methodList = new List<IOSCMethod>();
methodList.Add(new OSCMethod<float>() { Name = "/1/button1", Value = 0 });
methodList.Add(new OSCMethod<float[]>() { Name = "/1/array1", Value = new float[10] });
And it's this methodList which is what I'm trying to serialize. But everytime I try, either I get a "Can't serialize an interface" but when I make it (either the IOSCMethod or the OSCMethod<T> class) implement IXmlSerializable I get the problem of "can't serialize an object with a parameterless constructor. but obviously I can't because it's an interface! lame pants.
Any thoughts?
Is this what you want:
[TestFixture]
public class SerializeOscTest
{
[Test]
public void SerializeEmpTest()
{
var oscMethods = new List<OscMethod>
{
new OscMethod<float> {Value = 0f},
new OscMethod<float[]> {Value = new float[] {10,0}}
};
string xmlString = oscMethods.GetXmlString();
}
}
public class OscMethod<T> : OscMethod
{
public T Value { get; set; }
}
[XmlInclude(typeof(OscMethod<float>)),XmlInclude(typeof(OscMethod<float[]>))]
public abstract class OscMethod
{
}
public static class Extenstion
{
public static string GetXmlString<T>(this T objectToSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(objectToSerialize.GetType());
StringBuilder stringBuilder = new StringBuilder();
string xml;
using (var xmlTextWriter = new XmlTextWriter(new StringWriter(stringBuilder)))
{
xmlSerializer.Serialize(xmlTextWriter, objectToSerialize);
xml = stringBuilder.ToString();
}
return xml;
}
}

Categories

Resources