I have the following class:
[Serializable]
public class ClassOne : ClassTwo
{
private string _parameterOne;
private string _parameterTwo;
private string _parameterThree;
public Category (string parameterOne, string parameterTwo, string parameterThree)
{
_parameterOne = parameterOne;
_parameterTwo = parameterTwo;
_parameterThree = parameterThree;
}
}
I then want to make use of the XMLSerializer:
private void Serialize()
{
XmlSerializer xmlSerializer = new xmlSerializer(typeof(ClassOne));
xmlSerializer.Serialize(stream, object);
}
However, I can't serialize that class because it does not have a parameterless construction... How can I practically resolve this situation?
Add empty constructor. And you should use public properties for fields that you want to serialize
[Serializable]
public class ClassOne : ClassTwo
{
private string _parameterOne;
private string _parameterTwo;
private string _parameterThree;
public Category() { }
public Category (string parameterOne, string parameterTwo, string parameterThree)
{
_parameterOne = parameterOne;
_parameterTwo = parameterTwo;
_parameterThree = parameterThree;
}
}
Related
I am trying to get an object to deserialize into itself. I have tride the following:-
public class JobID
{
public string jobname;
public string first;
public string second;
public string third;
public string clientName;
public string workflow;
}
public void load(string fname)
{
string s = File.ReadAllText(fname);
this = JsonConvert.DeserializeObject<JobID>(s);
}
But the word this is 'read only' according to the error I get.
I have used 'this.jobname = "X";' before so clearly 'this' is not read only.
I am using Newtonsof.Json.
Why not use static method to load the object. Such as :
public class JobID
{
public string jobname;
public string first;
public string second;
public string third;
public string clientName;
public string workflow;
public static JobId Load(string fname){
string s = File.ReadAllText(fname);
return JsonConvert.DeserializeObject<JobID>(s);
}
}
Although you can assign a value to a property of 'this', you can't change the object to which 'this' refers to.
The 'this' keyword refers to the current object instance in the context (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/this).
Assuming the Load function is inside another object (or static), you could do something like:
public class JobID
{
public string jobname;
public string first;
public string second;
public string third;
public string clientName;
public string workflow;
}
public class JobReader
{
// Property to store deserialized object
public JobID Job { get; set; }
public void load(string fname)
{
string s = File.ReadAllText(fname);
// Assign object to property.
this.JobID = JsonConvert.DeserializeObject<JobID>(s);
}
}
You can use the static method as suggested by Xbotter to create a new instance. But for some reason if you want to deserialize the file content into current object only, then easiest way would be:
public class JobID
{
public string jobname;
public string first;
public string second;
public string third;
public string clientName;
public string workflow;
public void load(string fname)
{
string s = File.ReadAllText(fname);
JobID tmp = JsonConvert.DeserializeObject<JobID>(s);
copy(tmp);
}
public void copy(JobID tmp)
{
this.jobname = tmp.jobname;
// do the same for other properties that you want to copy
}
}
If you want to do this without a static method, you can use the JsonSerializer.Populate method. Example code:
public class JobID
{
public string jobname;
public string first;
public string second;
public string third;
public string clientName;
public string workflow;
public void load(string fname)
{
string s = File.ReadAllText(fname);
new JsonSerializer().Populate(new JsonTextReader(new StringReader(s)), this);
}
}
Anssssss provided the answer I've been looking for years. Thank you!
This is how I used it:
bool bOk = false;
StreamReader myFileStream = null;
Newtonsoft.Json.JsonSerializer mySerializer = new Newtonsoft.Json.JsonSerializer();
try
{
myFileStream = File.OpenText(sFilePath);
mySerializer.Populate(myFileStream, this);
bOk = true;
}
catch (Exception e)
{
string sException = e.ToString();
CHelper.ThrowException(new Exception(sException + "\n\n" + sFilePath));
}
finally
{
if (myFileStream != null)
{
myFileStream.Close();
}
}
return bOk;
I create a class named MyMainClass
namespace MyTest
{
private void frmMain_Load(object sender, EventArgs e)
{
MyMainClass myVar = new MyMainClass();
myVar.sub1.sb1A = "value 1A";
myVar.sub1.sb1B = "value 1B";
MessageBox.Show(myVar.sub2.wantPassString);
//I want to print the value "I've got value value 1A"
}
public class MyMainClass
{
public subClass1 sub1 = new subClass1();
public subClass2 sub2 = new subClass2();
public class subClass1
{
public string sb1A{get;set;}
public string sb1B{get;set;}
}
public class subClass2
{
public string sb2A{get;set;}
public string sb2B{get;set;}
// a1a is a value that I want to get from subClass1
string a1a = subClass1.sb1A;
public string wantPassString {get{return "I've got value " + a1a;}}
}
}
}
How can I pass the value from subClass1.sb1A to the string a1a or wantPassString in subClass2 ? when I call in frmMain_Load
The normal approach would be to pass the class through the constructor. This works:
public class MyMainClass
{
public MyMainClass()
{
sub1 = new subClass1();
sub2 = new subClass2(sub1);
}
public subClass1 sub1;
public subClass2 sub2;
public class subClass1
{
public string sb1A{get;set;}
public string sb1B{get;set;}
}
public class subClass2
{
public subClass2(subClass1 sub1)
{
this.sub1 = sub1;
}
public string sb2A{get;set;}
public string sb2B{get;set;}
subClass1 sub1;
public string wantPassString {get{return "I've got value " + sub1.sb1A;}}
}
}
Create a third class to hold the data.
public MainClass
{
private class sharedClass
{
internal string sb1A { get; set; }
}
public class subClass1
{
private readonly sharedClass _shared;
internal subClass1(sharedClass shared)
{
this._shared = shared;
}
public string sb1A
{
get
{
return this._shared.sb1A;
}
set
{
this._shared.sb1A = value;
}
public string sb1B{get;set;}
}
public class subClass2
{
private readonly sharedClass _shared;
public subClass2(shared s)
{
_shared = s;
}
public string sb2A{get;set;}
public string sb2B{get;set;}
public string wantPassString {get{return "I've got value " + _shared.sb1;}}
}
private readonly sharedClass _shared = new sharedClass();
private readonly subClass1 _subClass1;
private readonly subClass2 _subClass2;
public MainClass()
{
this._subClass1 = new subClass1(this._shared);
this._subClass2 = new subClass2(this._shared);
}
}
You can add additional property and method and pass it as a parameter:
public string sb2C{ get; set; }
public string getData(string strParam)
{
return sb2C= strParam;
}
Then on your frmMain_Load:
MessageBox.Show(myVar.sub2.getData(myVar.sub1.sb1B));
//This would also populate sb2C that will hold the data from the other class
I have multiple classes like:
public class Base { }
public class Base1: Base { public static List<Base1> LoadFromXml(string path) }
public class Base2: Base { public static List<Base2> LoadFromXml(string path) }
Then I want to have a method like this:
public List<T> PrepareBase<T>() where T: Base { return T.Load("C:\test.xml"); }
So that I don't have to make a method for every type.
But I don't know how to accomplish this or something similar.
The problem is that I can't make the LoadFromXml method known to the base class because static inheritance is not a thing. Neither is creating a seperate interface with a static method.
Is there a way to do this or am I expecting too much?
Edit:
An example of the LoadFromXml method:
public class Base1
{
public int ID { get; set; }
public string PropertyOnlyInBase1 { get; set; }
public static List<Base1> LoadFromXml(string path)
{
List<Base1> baseList = new List<Base1>();
XDocument doc = XDocument.Load(path);
foreach(var node in doc.Descendants("Base1"))
{
Base 1 base = new Base1() { ID = node.Attributes["id"] };
base.PropertyOnlyInBase1 = node.Element("PropertyOnlyInBase1");
baseList.Add(base);
}
return baseList;
}
}
So the Base classes also have some unique properties. That's why I needed the inheritance thing in the first place.
One option is to add a GenericBase:
public abstract class Base
{
}
public static class GenericBase<T>
where T : Base
{
public static List<T> LoadFromXml(string path)
{
//Load from XML
}
}
public class Base1 : Base { }
public class Base2 : Base { }
public class Test //MainForm.cs class or whatever you want
{
public void Tester() //Load event handler or whatever you want
{
List<Base1> base1List = PrepareBase<Base1>();
}
public List<T> PrepareBase<T>() where T : Base
{ return GenericBase<T>.LoadFromXml("C:\test.xml"); }
}
Edit:
As D Stanley mentioned, it's not possible; but I made some work-around that could be helpful for you:
public abstract class Base
{
public static List<T> LoadFromXml<T>(string path) where T : Base, new()
{
List<T> baseList = new List<T>();
XDocument doc = XDocument.Load(path);
foreach (var node in doc.Descendants(typeof(T).Name))
{
T t = new T();
Dictionary<string, string> d = new Dictionary<string, string>();
foreach (var item in node.Elements())
d.Add(item.Name.ToString(), item.Value);
t.Load(d);
baseList.Add(t);
}
return baseList;
}
protected internal abstract void Load(Dictionary<string, string> elements);
}
public class Base1 : Base
{
public string CustomProp1 { get; set; }
public string CustomProp2 { get; set; }
public string CustomProp3 { get; set; }
protected internal override void Load(Dictionary<string, string> elements)
{
if (elements.ContainsKey("CustomProp1"))
CustomProp1 = elements["CustomProp1"];
if (elements.ContainsKey("CustomProp2"))
CustomProp2 = elements["CustomProp2"];
if (elements.ContainsKey("CustomProp3"))
CustomProp3 = elements["CustomProp3"];
}
}
public class Base2 : Base
{
public string CustomProp1 { get; set; }
public string CustomProp2 { get; set; }
public string CustomProp3 { get; set; }
protected internal override void Load(Dictionary<string, string> elements)
{
if (elements.ContainsKey("CustomProp1"))
CustomProp1 = elements["CustomProp1"];
if (elements.ContainsKey("CustomProp2"))
CustomProp2 = elements["CustomProp2"];
if (elements.ContainsKey("CustomProp3"))
CustomProp3 = elements["CustomProp3"];
}
}
public class Test //MainForm.cs class or whatever you want
{
public void Tester() //Load event handler or whatever you want
{
List<Base1> base1List = PrepareBase<Base1>();
}
public List<T> PrepareBase<T>() where T : Base, new()
{
return Base.LoadFromXml<T>("C:\test.xml");
}
}
I think you're correct that the class that loads these from XML should be separate from the class that's being loaded. As you said, it has no real connection to the instance.
Perhaps what you need is a separate class that loads those instances for you.
public class BaseXmlLoader<TBase> where TBase : Base
{
public List<TBase> LoadFromXml(string filePath)
{
var serializer = new XmlSerializer(typeof(TBase));
// Load your file and deserialize.
}
}
The benefits aren't huge because it's not saving you that much code. But if the LoadFromXml methods are essentially the same except for the type then you're getting something out of it.
I changed my approach to the problem and solved it using the Factory pattern. I also provided each class with an instance method SetPropertiesFromXml to handle the custom properties. Unlike the previously used method, a method like that made sense as an instance method.
Factory:
public static class BaseFactory
{
public static Base GetBase(string id)
{
switch(id) { case '1': return new Base1(); ... }
}
public static T GetBaseList<T>(string xml, string tagName) where T: Base
{
List<T> list = new List<T>();
var nodes = XDocument.Load(xml).Descendants(tagName);
foreach(XElement node in nodes)
{
var base = GetBase(node.Attribute("id").Value);
base.SetPropertiesFromXml(node);
list.Add(base as T);
}
}
}
Bases
public abstract class Base
{
public virtual void SetPropertiesFromXml(XElement node)
{
//<Set Properties like: this.Property = node.Element("key");>
}
}
public class Base1
{
public override void SetPropertiesFromXml(XElement node)
{
//<Set Custom Properties for Base1>
//Call Base to add the normal properties as well
base.SetPropertiesFromXml(node);
}
}
Call
List<Base1> list = BaseFactory.GetBaseList<Base1>("test.xml", "Base1");
I have the following base class:
public class Base
{
public string LogicalName { get; set; }
public int NumberOfChars { get; set; }
public Base()
{
}
public Base(string logicalName, int numberOfChars)
{
LogicalName = logicalName;
NumberOfChars = numberOfChars;
}
}
and the following derived classes:
public class Derived1 : Base
{
public const string EntityLogicalName = "Name1";
public const int EntityNumberOfChars = 30;
public Derived1() : base(EntityLogicalName, EntityNumberOfChars)
{
}
}
public class Derived2 : Base
{
public const string EntityLogicalName = "Name2";
public const int EntityNumberOfChars = 50;
public Derived2()
: base(EntityLogicalName, EntityNumberOfChars)
{
}
}
and I also have this function that is provided by a service:
public IEnumerable<T> GetEntities<T>(string entityName, int numberOfChars) where T : Base
{
//Some code to get the entities
}
My problem is how can I call this function generically? I want to call it with something that looks like this:
public void TestEntities<T>() where T : Base
{
var entities = GetEntities<T>(T.EntityLogicalName, T.EntityNumberOfChars);
//some other code to test the entities
}
This of course doesn't work because at this point T is not known. How can I accomplish something similar to this? EntityLogicalName and EntityNumberOfChars are characteristics that all Base derived classes have and they never change for each derived class. Can I get them from the Base class without instantiating objects or some other way that I am not seeing?
Replace constants with getter abstract properties
public abstract class Base
{
public abstract string LogicalName { get; }
public abstract int NumberOfChars { get; }
public Base()
{
}
}
public class Derived1 : Base
{
public string LogicalName { get { return "Name1"; } }
public int NumberOfChars { get { return 30; } }
public Derived1() : base()
{
}
}
Also, you will be able to put some logic into overriden getter, e.g. :
...
public string LogicalName { get { return this.EntityMap.Name; } }
...
UPDATE: The fact that you do not want to instantiate object from class but want to be able to get that string in a strongly typed manner can be handled in one more way. It is totally separate from answer above ( Since you can't override static props in c#). Consider the following code. We are adding one more class here, but LocatorInner can be a member of BaseClass. We are using this approach a lot in several existing apps.:
public class Locator
{
public static class LocatorInner<T> where T : BaseClass
{
public static string Name { get; set; }
}
public static string GetName<T>() where T : BaseClass
{
return LocatorInner<T>.Name;
}
public static void SetName<T>(string name) where T : BaseClass
{
LocatorInner<T>.Name = name;
}
}
public class BaseClass
{
}
public class DerivedClass: BaseClass
{
static DerivedClass()
{
Locator.LocatorInner<DerivedClass>.Name = "me";
}
}
public class TestClass<T> where T : BaseClass
{
public void Method()
{
var name = Locator.GetName<T>();
}
}
IMHO, I believe using constants here is a bad design decision.
You can either solve the issue using #vittore approach, but for me it sounds like you should use meta-programming with attributes if you're looking to get data from the T generic argument
For example, what about:
public class LogicalNameAttribute : Attribute
{
public LogicalNameAttribute(string name)
{
Name = name;
}
public string Name { get; private set; }
}
public class NumberOfCharsAttribute : Attribute
{
public NumberOfCharsAttribute (int number)
{
Number = number;
}
public string Number { get; private set; }
}
[LogicalName("Name1"), NumberOfChars(30)]
public class Derived1 : Base
{
public Derived1() : base()
{
}
}
Now your service method can extract attribute metadata as follows:
public void TestEntities<T>() where T : Base
{
LogicalNameAttribute logicalNameAttr = typeof(T).GetCustomAttribute<LogicalNameAttribute>();
NumberOfCharsAttribute numberOfCharsAttr = typeof(T).GetCustomAttribute<NumberOfCharsAttribute >();
Contract.Assert(logicalNameAttr != null);
Contract.Assert(numberOfCharsAttr != null);
string logicalName = logicalNameAttr.Name;
int numberOfChars = numberOfCharsAttr.Number;
// Other stuff
}
There's a performance penalty because you need to use reflection to get attributes applied to T, but you gain the flexibility of not forcing derived classes to provide this static info.
As #vittore mentioned, move the properties to base,pass the hard coded values from derived and in creation use just defautl(T)
public IEnumerable<T> GetEntities<T>(string entityName, int numberOfChars) where T : Base
{
yield return default(T); //Is its always class use new constraint and return new T();
}
I am serializing with xml, and I had it working with just a simple class, but when I made a secondary class, of which the simple class was just a component, the serialization stopped working. It fails with an "Error reflecting type" error at the serialization stage. The code is as follows:
public class CustomField
{
[XmlAttribute("FieldID")]
public string FieldID;
[XmlAttribute("FieldValue")]
public string FieldValue;
public CustomField() { }
public CustomField(string fieldID, string fieldValue)
{
this.FieldID = fieldID;
this.FieldValue = fieldValue;
}
}
[XmlType("Entry")]
public class CustomEntry
{
[XmlAttribute("Author")]
public string Author;
[XmlAttribute("Title")]
public string Title;
[XmlAttribute("Trial")]
public string Trial;
[XmlAttribute("Responses")]
public List<CustomField> Responses;
public CustomEntry() { }
}
public static class EntrySerializer
{
public static void SerializeObject(this CustomEntry entry, string file)
{
var serializer = new XmlSerializer(typeof(CustomEntry));
using (var stream = File.OpenWrite(file))
{
serializer.Serialize(stream, entry);
}
}
}
Is it a labeling issue with the Xml markers, or is it something else?
Try defining your serializer like this:
var serializer = new XmlSerializer(typeof(CustomEntry), new Type[] { typeof(CustomField) });
You need to inform the serializer of the additional types it is expecting to serialize.
I usually tag with XMLRoot (two places). I need to see sample of XML to give better answer.
[XmlRoot("CustomField")]
public class CustomField
{
[XmlAttribute("FieldID")]
public string FieldID;
[XmlAttribute("FieldValue")]
public string FieldValue;
public CustomField() { }
public CustomField(string fieldID, string fieldValue)
{
this.FieldID = fieldID;
this.FieldValue = fieldValue;
}
}
[XmlRoot("Entry")]
public class CustomEntry
{
[XmlAttribute("Author")]
public string Author;
[XmlAttribute("Title")]
public string Title;
[XmlAttribute("Trial")]
public string Trial;
[XmlAttribute("Responses")]
public List<CustomField> Responses;
public CustomEntry() { }
}