Using reflection to display data from different custom classes - c#

I want to create a method that displays the information contained in an object, that will work dynamically, with any object. I'm having trouble handling properties that are other custom classes. In the example below the Person has Phones and Occupations which both are other classes. When the data is displayed, the value on the screen currently is:
TestReflection.Person
Name: Mary
Phones: TestReflection.Phones
Occupations: TestReflection.Occupations
It just displays the name of class, like TestReflection.Phones, rather than the data inside that object.
How can I change this code to show information like this instead?
TestReflection.Person
Name: Mary
Phones:
TestReflection.Phones
Type: 1
Number: 555XYZ
Occupations:
TestReflection.Occupations
Type: 5
Description: Secretary
Here is my code:
class Program
{
static void Main(string[] args)
{
List<Person> listPeson = new List<Person>();
var person1 = new Person();
person1.Name = "Mary";
person1.Phones = new Phones { new Phone { Type = 1, Number = "555XYZ" } };
person1.Occupations = new Occupations {new Occupation { Type = 5, Description = "Secretary" }};
listPeson.Add(person1);
DynamicExport(listPeson);
Console.ReadLine();
}
public static void DynamicExport<T>(List<T> listReg)
{
for (int i = 0; i < listReg.Count; i++)
{
Console.WriteLine(listReg[i].GetType());
foreach (var item in listReg[i].GetType().GetProperties())
{
Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");
}
}
}
}
class Person
{
public string Name { get; set; }
public Phones Phones { get; set; }
public Occupations Occupations { get; set; }
}
class Phones : List<Phone> { }
class Phone
{
public int Type { get; set; }
public string Number { get; set; }
}
class Occupations : List<Occupation> { }
class Occupation
{
public int Type { get; set; }
public string Description { get; set; }
}

I made some edits to your question - I hope I understood you correctly.
If you want to export data
If your question is really about displaying data, then there are better ways to do it than creating your own export method. The format you are trying to display looks similar to YAML. There's also JSON and XML. Using one of these libraries is probably better than writing your own method:
YamlDotNet NuGet package
Json.NET NuGet Package
System.Xml.Serialization.XmlSerializer class
If you want to learn more about reflection
Maybe you're interested in learning more about reflection, and the export is just an example to play around with it. In that case, let's look at this line:
Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");
$"{item.GetValue(listReg[i], null)}" ends up calling person1.Phones.ToString(). The default behavior of ToString just displays the type name. You could override that behavior, like this:
class Phones : List<Phone>
{
public override string ToString()
{
return Program.DynamicExportToString(this);
// ... where DynamicExportToString is a modified version of DynamicExport that
// builds and returns a string rather than sending it directly to the Console.
}
}
Maybe you want to be able to handle any class, even when you cannot override ToString in all of the classes you might export. Then you will need to put some additional logic in the DynamicExport method, because...
$"{item.Name}: {item.GetValue(listReg[i], null)}"
... doesn't work for every situation. We need to display different things depending on the type of the property.
Consider how you want to handle null values. Maybe something like $"{item.Name}: <null>"
Use your existing $"..." code if the type is...
a primitive type.
DateTime
String
... or a Nullable<> of one of those types.
If the type implements IEnumerable, loop over the contents of the collection and recursively call your export code for each element.
It's important to check for this interface after you've checked if the type is a String, because String implements IEnumerable.
Otherwise, recursively call your export code on this value.
When you call your export code recursively, it would be wise to guard against infinite loops. If the object you're trying to export contains a circular reference - you could quickly wind up with a StackOverflowException. To avoid this, maintain a stack of objects that have already been visited.
I think the above advice is generally applicable whenever you're using reflection to traverse an object graph - whether it's for serialization or any other purpose.
I hope this helps!

Related

JSON to C# conversion with nested classes including arrays

Summary
MANASYS Jazz generates CICS (= mainframe) Web services as COBOL programs that communicate using JSON, and it also generates related C# interfaces that expose these services to client programs as properties and methods. Program JSPG2 responds with a single employee record, JSPG2A is a version that can return up to 10 Employee records. JSPG2 works perfectly (see this video), but JSPG2A does not.
Detail
To map hierarchical COBOL record structures to C# classes, a set of nested C# classes is defined like this
namespace MyJSv
{
public class ResponseJSPG2A
{
public class JSPG2AResponse_
{
public class OJSPG2A_
{
//...
public int JZ_Employee_BrowseCount { get; set; }
// and more scalar classes,
public class JZ_Employee_
{
public string JZ_Employee_NthReturnCode { get; set; }
public string EMPNO { get; set; }
// and more classes within the Employee record
}
public JZ_Employee_[] JZ_Employee { get; } = new JZ_Employee_[10];
}
public OJSPG2A_ OJSPG2A { get; } = new OJSPG2A_ ();
}
public JSPG2AResponse_ JSPG2AResponse { get; } = new JSPG2AResponse_ ();
}
}
For a single occurrence of JZ-Employee its definition is
public JZ_Employee_ JZ_Employee { get; } = new JZ_Employee_ ();
Otherwise the definition of ResponseJSPG2 and ResponseJSPG2A are the same
Newtonsoft.JSON converts the incoming JSON to C# with
Response = JsonConvert.DeserializeObject<ResponseJSPG2>(result);
and then
AssignResponseToProperties(true);
assigns data from ResponseJSPG2 to the properties that JSPG2Client wishes to expose.
This all works perfectly with ResponseJSPG2, where there is a single Employee record. The equivalent with <ResponseJSPG2A> where there is an array of 10 records returns 10 null records, and the equivalent AssignResponseToProperties fails when it attempts to reference to an Employee field: -
_EMPNO = Response.JSPG2AResponse.OJSPG2A.JZ_Employee[EmployeeSub].EMPNO;
Yet Visual Studio debugging, and the same test of the web service program JSPG2A with test utility ReadyAPI, shows that JSON is returned with 10 Employee records, all containing the expected data.
Here is the JSON returned to the test, edited to remove most fields and only 2 Employee records, so that it matches the nested C# classes above: -
{
"JSPG2AResponse": {
"OJSPG2A": {
"JZ_Employee_BrowseCount": 16,
"JZ_Employee": [
{
"JZ_Employee_NthReturnCode": "F",
"EMPNO": "000060"
},
{
"JZ_Employee_NthReturnCode": "N",
"EMPNO": "000090"
}
]
}
}
}
Question
Is there a way of getting DeserializeObject to handle array classes automatically, or do I have to use Newtonsoft.Json.Linq and write logic to parse the JSON and handle the array explicitly? Or some other way of achieving my objective?
Your problem is that the JZ_Employee property is an array-valued property that lacks a setter:
public JZ_Employee_[] JZ_Employee { get; } = new JZ_Employee_[10];
Because .Net arrays cannot be resized, Json.NET treats them as read-only collections whose contents need to be accumulated into a temporary list which is then used to construct the array and set it back in its parent. However, your array property has no setter, so Json.NET sees it as completely read-only and skips it entirely.
To resolve this problem, you could add a setter. It could be protected or private if you mark the property with [JsonProperty]:
[JsonProperty]
public JZ_Employee_[] JZ_Employee { get; private set; } = new JZ_Employee_[10];
Demo fiddle #1 here.
Alternatively, you could leave the property as get-only but replace the array with a resizable collection such as List<JZ_Employee_>:
public List<JZ_Employee_> JZ_Employee { get; } = new List<JZ_Employee_>();
Demo fiddle #2 here.
This second option has the advantage that you can apply [JsonConverter(typeof(SingleOrArrayConverter<JZ_Employee_>))] or just [JsonConverter(typeof(SingleOrArrayListConverter))] from the answers to How to handle both a single item and an array for the same property using JSON.net to JZ_Employee, which will allow you to merge the ResponseJSPG2 and ResponseJSPG2A classes:
[JsonConverter(typeof(SingleOrArrayConverter<JZ_Employee_>))]
public List<JZ_Employee_> JZ_Employee { get; } = new List<JZ_Employee_>();
The converter will automatically convert the case of a single JZ_Employee object into a list with one item.

List<CustomClass> sent as List<T>; how to get the properties?

I have this piece of code
public class Ticket
{
public string strArticleID { get; set; }
public string strArticleDescription { get; set; }
public decimal decArticlePrice { get; set; }
public decimal decArticleVAT { get; set; }
public decimal decArticuleNetPrice { get; set; }
public decimal decArticleDiscount { get; set; }
public decimal decArticleQuantity { get; set; }
}
public static List<Ticket> _lstCurrentTicket = new List<Ticket>();
That I want so send to an external DLL to get all the lines in _lstCurrentTicket to print a ticket through
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
Ticket ticket = new Ticket();
string strRefID = this.dataGridView1.Rows[i].Cells[0].Value.ToString();
string strDescription = this.dataGridView1.Rows[i].Cells[1].Value.ToString();
decimal decQuantity = (decimal)this.dataGridView1.Rows[i].Cells[2].Value;
decimal decUPrice = (decimal)this.dataGridView1.Rows[i].Cells[3].Value;
decimal decDiscount = Convert.ToDecimal(this.dataGridView1.Rows[i].Cells[4].Value.ToString().Substring(0, this.dataGridView1.Rows[i].Cells[4].Value.ToString().Length - 1));
decimal decVAT = Convert.ToDecimal(this.dataGridView1.Rows[i].Cells[5].Value.ToString().Substring(0, this.dataGridView1.Rows[i].Cells[5].Value.ToString().Length - 1));
decimal decGPrice = (decimal)this.dataGridView1.Rows[i].Cells[6].Value;
ticket.strArticleID = strRefID;
ticket.strArticleDescription = strDescription;
ticket.decArticlePrice = decUPrice;
ticket.decArticleVAT = decVAT;
ticket.decArticuleNetPrice = decGPrice;
ticket.decArticleDiscount = decDiscount;
ticket.decArticleQuantity = decQuantity;
_lstCurrentTicket.Add(ticket);
}
TicketPrinting tktPrint = new TicketPrinting ();
//Ticket and copies
tktPrint.PrintTicketFromList(_lstCurrentTicket, 2);
Since it is an external DLL, I thought the easiest way to work with it in target DLL was
public void PrintTicketFromList<T>(List<T> lstArticles, short intCopies)
{
foreach (var prop in lstArticles.GetType().GetProperties())
{
if (prop.Name == "Item")
{
//Copy external list to local class for printing
}
}...
But I'm stuck there. How can I iterate each property and value from each original class in the list so I can copy it? If I make a breakpoint I can see that the fields and values are correctly passed, but I do not get how to access them so I can do something like creating a local class exactly like the original and clone the list (and if I try it will say local list(Ticket) and passed List(T) are not the same type).
Or how could I copy it if I create an exact class in the target and do something like
public void PrintTicketFromList(object lstArticles, short intCopies)
{
List<TargetDLLTicket> lst =((List<TargetDLLTicket>)lstArticles).ToList(); }
Any thoughts?
It sounds like you have a circular dependency issue. You need to move the types you are sending to your print function to a common assembly (new project) that is then referenced by both the calling project and your print project. Then both projects can access this shared type.
A note about your design. The way you are going about this is probably not good to begin with thus your error. The actual printer function should not have to know anything about the types passed in. A good rule of thumb is to try to make your code as loosly coupled as possible. A better idea is to create an Interface that takes care of writing to the printer canvas (or something like that, you did not provide your printer code so this is a guess) and the printer function can call that method on the incoming object. The printer method should then also only accept that interface as a parameter. This is based on a Visitor pattern. Here is an example.
public interface IPrintable {
void WriteToPrinter(PrinterCanvas canvas);
}
public class Printer {
public void Print(IPrintable somethingToPrint) {
var canvas = getCanvas();
somethingToPrint.WriteToPrinter(canvas);
}
}
If at any point possible you should try to avoid reflection like Igor does in his answer.
But if you really want to use reflection you are currently not inspecting the item but the list of items.
You should try something like (writing this from memory):
public void PrintTicketFromList<T>(List<T> lstArticles, short intCopies)
{
foreach (var item in lstArticles)
{
foreach (var prop in typeof(T).GetProperties())
{
var value = prop.getValue(item);
}
}
}
Instead of List<T> create an interface, ITicket for example and accept List<ITicket>. Using List<T> as a generic whenever you know you only can work with something that is a Ticket is creating an unnecessary wide range of potential inputs. Using an interface allows you to not worry about the concrete implementation, and instead get at only what your dll is concerned with, the contract.
You could put the interface in one of two places, either another external common assembly that both of your assemblies reference, or you could put the interface into your assembly that has the ticket printing logic. Your Ticket class could then implement the interface.
An example of what this could look like:
public interface ITicket
{
//properties and methods you want to have all implementations to contain.
}
public class Ticket : ITicket
{
}
public class LastTicket :ITicket
{
}
public void PrintTicketFromList(List<ITicket> lstArticles, short intCopies)
{
}

FileHelpers error: The field: 'k__BackingField' has the type: XXX that is not a system type, so this field need a CustomConverter

I need to read a CSV file with FileHelpers based on type, automatically generated by my MVC model. The model looks like this:
public partial class Merchant
{
public long Id { get; set; }
public string Name { get; set; }
public Nullable<int> Category { get; set; }
public virtual MerchantCategory MerchantCategory { get; set; }
}
The last field is obviously generated by a foreign key in database, referring to table MerchantCategories.
Then I attempt to create an instance of FileHelperEngine with this type:
var engine = new FileHelperEngine<Merchant>();
And get the following exception:
The field: 'k__BackingField' has the type: MerchantCategory that is not a system type, so this field need a CustomConverter ( Please Check the docs for more Info).
Actually I don't need this field at all for my import, so I tried to ignore it in derived class:
[DelimitedRecord(",")]
public class MerchantForImport : Merchant {
[FieldHidden]
new public MerchantCategory MerchantCategory;
}
var engine = new FileHelperEngine<MerchantForImport>();
And still the same error. I don't need this field at all, I don't want to implement any FieldConverter for it, I never asked for this k__BackingField and it's nowhere to be found in my code!
I can't call FileHelperEngine.Options.RemoveField() because the exception is thrown by the constructor.
Where does that come from? How do I get rid of it?
From a design perspective, I think you are going about it the wrong way. You are trying to use the Merchant class for two incompatible uses. Instead you should have two separate classes.
FileHelpers is a library for describing csv files so that you can import them easily. You should have a MerchantFileSpec for describing your file. It's really not a proper C# class - it may have: dummy fields to represent unused columns; lots of attributes [FieldNullValue], [FieldQuoted], [FieldConverter]; etc. It works best with public fields (a FileHelpers limitation which is not C# best practice), etc. It is a convenience syntax for describing the import file. It should not include any business logic or special constructors, or backing fields. Keep it as simple as possible.
Then you can have your MVC-generated Merchant class which is separate. Its purpose is to describe the merchant as required by the MVC framework, with foreign keys, ids, whatever.
Then you use a FileHelperEngine<MerchantFileSpec> to read the records into an array and map it to an enumerable of Merchant (via Linq or a library like AutoMapper).
Something like:
/// Your MVC-generated class. Add methods, getters, setters, whatever.
/// FileHelpers doesn't use this class.
class Merchant
{
public long Id { get; set; }
public string Name { get; set; }
public Nullable<int> Category { get; set; }
public virtual MerchantCategory MerchantCategory { get; set; }
}
/// This is the class FileHelpers will use
/// This class describes the CSV file only. Stick to whatever
/// syntax conventions are required by FileHelpers.
[DelimitedRecord(";")]
class ProductMerchantFileSpec
{
[FieldQuoted(QuoteMode.OptionalForRead)]
public long Id;
[FieldQuoted(QuoteMode.OptionalForRead)]
public string Name;
[FieldQuoted(QuoteMode.OptionalForRead)]
// Handle non-US formats such as , decimal points
// convert from inches to centimetres?
// you get the idea...
[FieldConverter(MyCustomizedCategoryConverter)] // you get the idea
public int Category;
}
class Program
{
static void Main(string[] args)
{
var engine = new FileHelperEngine<ProductMerchantFileSpec>();
var productMerchantRecords = engine.ReadFile(filePath);
var productMerchants = productMerchantRecords
.Select(x => new Merchant() { Id = x.Id, Name = x.Name, Category = x.Category });
}
}
I received this error specifically because my object (i.e. Merchant) was missing a column that existed in the source file. I was able to work around the issue prior to realizing the missing column by adding a new property to my object class public string[] MyProperty { get; set; }. This work-around help me realize a column was missing.
i.e..
public partial class Merchant
{
public long id { get; set; }
..
..
..
public string[] MyProperty { get; set; }
}

Using ServiceStack OrmLite to create Key Value table for dynamic types

I want to create a key value table in my database along the lines of
public class KeyValue {
public string Id { get; set; }
public dynamic Value {get; set; }
}
Using a slightly modified SqlProvider I have no problems getting CreateTable<KeyValue>() to generate varchar(1024) Id, varchar(max) Value.
I have no issues saving objects to it. The problem is when I load the objects
var content = dbConn.GetById<KeyValue>("about");
content.Value at this point is a string.
Looking at the database record, the text for value does not appear to store any type information.
Is there really anything I can do better other than manually invoking ServiceStack.Text and call deserialize with the appropriate type information?
I do not need absolute dynamic, my actual use case is for polymorphism with a base class instead of dynamic. So I don't really care what type Value is whether it's the base class, dynamic, object, etc. Regardless other than using the class
public class KeyValue {
public string Id { get; set; }
public MySpecificChildType Value {get; set; }
}
I haven't been able to get anything other than a string back for Value. Can I tell OrmLite to serialize the type information to be able to correctly deserialize my objects or do I just have to do it manually?
Edit: some further information. OrmLite is using the Jsv serializer defined by ServiceStack.Text.TypeSerializer and is in no way pluggable in the BSD version. If I add a Type property to my KeyValue class with the dynamic Value I can do
var value = content.Value as string;
MySpecificChildType strongType =
TypeSerializer.DeserializeFromString(content, content.Type);
I just really want a better way to do this, I really don't like an object of 1 type going into the db coming back out with a different type (string).
I haven't worked much with the JsvSerializer but with the JsonSerializer you can achieve this (in a few different ways) and as of ServiceStack 4.0.11 you can opt to use the JsonSerializer instead, see https://github.com/ServiceStack/ServiceStack/blob/master/release-notes.md#v4011-release-notes.
Example
public abstract class BaseClass {
//Used for second example of custom type lookup
public abstract string Type { get; set; }
}
public class ChildA : BaseClass {
//Used for second example of custom type lookup
public override string Type { get; set; }
public string PropA { get; set; }
}
And then in your init/bootstrap class you can configure the serializer to emit the type information needed for proper deserialization:
public class Bootstrapper {
public void Init() {
ServiceStack.Text.JsConfig.ExcludeTypeInfo = false;
ServiceStack.Text.JsConfig.IncludeTypeInfo = true;
}
}
If you wish to use something other that the default "__type" attribute that ServiceStack uses (if you for example want to have a friendly name identifying the type rather then namespace/assembly) you can also configure your own custom type lookup as such
public class Bootstrapper {
public void Init() {
ServiceStack.Text.JsConfig.ExcludeTypeInfo = false;
ServiceStack.Text.JsConfig.IncludeTypeInfo = true;
ServiceStack.Text.JsConfig.TypeAttr = "type";
ServiceStack.Text.JsConfig.TypeFinder = type =>
{
if ("CustomTypeName".Equals(type, StringComparison.OrdinalIgnoreCase))
{
return typeof(ChildA);
}
return typeof(BaseClass);
}
}
}

Method to handle objects with properties in common, but different object types

I have a large collection of automatically generated objects. Although they are all of different, non-related classes, all of the objects share some basic properties (name, id, etc.). I do not control the generation of these objects, so unfortunately I cannot take the ideal approach of implementing an interface. I would like to create a method in which I pass an arbitrary one of these objects and do something using these common properties.
The general idea would be something like:
someObj a = new someObj();
a.name = "sara";
diffObj b = new diffObj();
b.name = "joe";
string phrase = string.Format("I am with {0} and {1}",
getName(a), getName(b));
private string getName(object anyObjWithName)
{
return anyObjWithName.name;
}
though naturally this does not work.
I thought a generic method might hold the answer, but the only way I can see to call it with the current type is using genericMethod.Invoke , which still carries the same issue of not being able to resolve the properties of the passed object in the method. This is unlike Calling generic method with a type argument known only at execution time or How to call generic method with a given Type object? where only the type, or properties of the type, are used in the method, as opposed to properties of the object.
I am aware that this would be (very) prone to error, but I can guarantee that all objects encountered will have the common properties being manipulated.
I can guarantee that all objects encountered will have the common properties being manipulated
If that's the case, you can use dynamic:
private string getName(dynamic anyObjWithName)
{
return anyObjWithName.name;
}
Be aware that using any object that does not have a name property will not fail until run-time.
If you want to add a little bit of safety you can catch the RuntimeBinderException that gets thrown if the property does not exist:
private string getName(dynamic anyObjWithName)
{
try {
return anyObjWithName.name;
}
catch(RuntimeBinderException) {
return "{unknown}";
}
}
If you're unhappy with the performance using dynamic as mentioned by D Stanley, you could always try FastMember.
All you need to know to start using it is pretty much shown in the first 2 code examples.
You are creating a Rube Goldberg device there. You should just have all your data objects classes implement a single interface, then you can work on that. Much simpler and less error prone than fiddling with reflection.
The very fact that a lot of objects have common properties but don't share the same ancestry, on in the very least a common interface, shows that something is wrong with your design. Do rethink it.
Multiple ways to accomplish this, simplest probably is to create Interface and declare common methods there, have your object implement it, then change "getName" method take interface object
private string getName(IMyInterface anyObjWithName)
{
return anyObjWithName.name;
}
The correct way to do this is with an interface, if you own the types that you're working with
public interface IEntity
{
int ID { get; set; }
string Name { get; set; }
}
public class TypeOne : IEntity
{
public int ID { get; set; }
public string Name { get; set }
public string BespokePropertyOne { get; set;}
}
public class TypeTwo : IEntity
{
public int ID { get; set; }
public string Name { get; set; }
public float BespokePropertyTwo { get; set; }
}
static void Main(string[] args)
{
List<IEntity> entities = new List<IEntity>();
entities.Add(new TypeOne() { ID = 1, Name = "Bob", BespokePropertyOne = "blablabla" });
entities.Add(new TypeTwo() { ID = 2, Name = "Alice", BespokePropertyTwo = 5.4f });
foreach (IEntity entity in entities)
{
Console.WriteLine("ID: {0} Name: {1}", entity.ID, entity.Name);
}
}
This answer was written before the edit to the question stating that interfaces weren't possible in this case. Perhaps it can help someone else reading this question.
Interface:
interface Iname
{
string Name { get; set; }
}
Use interface:
class A : Iname
{
public string Name { get; set; }
}
class B : Iname
{
public string Name { get; set; }
}
The method:
string GetName(Iname o)
{
return o.Name;
}
Use:
A a = new A { Name = "First" };
B b = new B { Name = "Last" };
Text = GetName(a) + " " + GetName(b);

Categories