Best way to present the user the XML values of a file? - c#

I got a XML File, looking familiar to this :
<root>
<carnumber>12</carnumber>
<carcolor>2</carcolor>
<cartype>5</cartype>
</root>
Like you see I got some Elements with values/text in it. The car element for example can take values from 1 to 1000. But the element carcolor can take values from 1 - 5 and the cartype from 1 - 10.
The important thing is that the values of the carcolor and cartype elements mean something. carcolor "2" means red, "1" blue and so on.
So I need to present the user not the values but the real meaning of the values.
I found myself creating some classes that represent the elements with there valid values and things got really complicated and I dont know if this was/is the best way.
A friend of mine suggested me to use XML serialization because my XML file is static. It will never change.
My question is simple. I just wanna know how you would solve this problem. My idea contains classes that represent the XML element, for example cartype within this class I have a Dictonary with a pair. This represent the values within the XML file and the string is the meaning of this value. And I use a lot of Linq to navigate and edit the values.
Thanks again!

This can be as complicated or easy as you want it to be. I would, however, second your friends suggestion to use XML Serialization, something like:
[XmlRoot("Car")]
public class Car
{
public Car()
{
}
[XmlElement("Number")]
public int Number { get; set; }
[XmlElement("Color")]
public int Color { get; set; }
[XmlElement("Type")]
public int Type { get; set; }
}
Serialization:
Car myCar = new Car();
myCar.Number = 1;
myCar.Color = 2;
myCar.Type = 3;
XmlSerializer s = new XmlSerializer(typeof(Car));
TextWriter w = new StreamWriter( #"c:\Car.xml" );
s.Serialize(w, myCar);
w.Close();
Deserialization:
Car myCar;
TextReader r = new StreamReader("Car.xml");
myCar = (Car)s.Deserialize(r);
r.Close();
You could further improve this by exposing a custom enum for the likes of your Type field and internally serializing the number relating to it. Also perhaps exposing the Color enum for the car and internally storing a numerical value.

Give this a try:
[XmlRoot("root")]
public class Car
{
private static XmlSerializer serializer = new XmlSerializer(typeof(Car));
[XmlElement("carnumber")]
public int Number { get; set; }
[XmlElement("carcolor")]
public int Color { get; set; }
[XmlElement("cartype")]
public int Type { get; set; }
[XmlIgnore]
public CarColor CarColor
{
get
{
return (CarColor)Color;
}
set
{
Color = (int)value;
}
}
[XmlIgnore]
public CarType CarType
{
get
{
return (CarType)Type;
}
set
{
Type = (int)value;
}
}
public string CarColorString
{
get
{
return this.CarColor.ToString().Replace('_', ' ');
}
}
public string CarTypeString
{
get
{
return this.CarType.ToString().Replace('_', ' ');
}
}
public string Serialize()
{
StringBuilder sb = new StringBuilder();
using (StringWriter writer = new StringWriter(sb))
{
serializer.Serialize(writer, this);
}
return sb.ToString();
}
public static Car Deserialize(string xml)
{
using (StringReader reader = new StringReader(xml))
{
return (Car)serializer.Deserialize(reader);
}
}
}
public enum CarColor
{
Red = 1,
Blue = 2,
Green = 3,
Light_Brown = 4
// and so on...
}
public enum CarType
{
Sedan = 1,
Coupe = 2,
Hatchback = 3,
SUV = 4,
Pickup_Truck = 5
// and so on...
}
I've added some enums to allow for presentation.
You can set the values of a Car and serialize it to an xml string:
Car car = new Car();
car.Number = 1;
car.CarColor = CarColor.Blue;
car.CarType = CarType.Coupe;
string xml = car.Serialize();
And deserialize an xml string into a car:
string example =
#"<root>
<carnumber>12</carnumber>
<carcolor>2</carcolor>
<cartype>5</cartype>
</root>";
Car car = Car.Deserialize(example);
For presentation, you can use the CarColorString and CarTypeString properties, which, in case your enum values contain more than one word, replace underscores with spaces.
Console.WriteLine(car.CarColorString);
Console.WriteLine(car.CarTypeString);

Why not format your XML file more like:
<root>
<carnumber code="2" name="John's Car"/>
<carcolor code="3" name="Red"/>
<cartype code="2" name="Hatchback"/>
</root>

I would build the UI in WPF, not WinForms. Set up a data context that binds to the XML as an XML data source, write type converters to round trip the data between the internal and human-readable values, bind combo boxes to the various elements, and Bob's your uncle.
I recognize that this answer isn't very useful to you if you're not using WPF.

Related

C# get if a property is any sort of list

I'm trying to make a text writer according to my classes properties in the following pattern:
MyClass
ID 1
Name MyName
AnotherProperty SomeValue
ThisIsAnotherClass
AnotherClassProperties 1
//Example class
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
public string AnotherProperty { get; set; }
public AnotherClass ThisIsAnotherClass { get; set; }
}
So I'm taking each property name, writing it, a blank space, then it's value (if there is any).
Now I'm trying to implement support for lists and anything array-like for something like this:
MyClass
ArrayTest
1
2
3
If it's a class, I'll have a recursive for the function so I can display all values inside the list/array in this pattern. (it's for a webservice)
My question is, how can I find if a specific property is something list-able?
I've tried:
Type type = myObject.GetType();
PropertyInfo[] properties = type.GetProperties();
for(int i = 0; i < properties.Length; i++)
{
if(properties[i].PropertyType.IsGeneric) //Possible List/Collection/Dictionary
{
//Here is my issue
Type subType = properties[i].PropertyType.GetGenericTypeDefinition();
bool isAssignable = subType.IsAssignableFrom(typeof(ICollection<>)); //Always false
bool isSubclass = subType.IsSubclassOf(typeof(ICollection<>)); //Always false
//How can I figure if it inherits ICollection/IEnumerable so I can use it's interface to loop through it's elements?
}
else if(properties[i].PropertyType.IsArray) //Array
{
}
else if(properties[i].PropertyType.IsClass && !properties[i].PropertyType.Equals(typeof(String)))
{
//Non-string Subclasses, recursive here
}
else
{
//Value types, write the text + value
}
}
Like mentioned in the comments: use Json as a way to format objects, it will save a lot of time.
If you have reason not to do this, you can check if the type is enumerable: This also covers the Type.IsArray case.
typeof(IEnumerable).IsAssignableFrom(properties[i].PropertyType)
As an added notice of caution: maybe you do not want to enumerate String and byte[] type objects.

Is it possible to serialize a custom struct as an xml attribute?

Is is possible to serialize a custom struct as an xml attribute?
Sample code:
public class Dummy<T>
{
private Dummy() { }
public Dummy(T item1)
{
Item1 = item1;
Item2 = item1;
}
public T Item1 { get; set; }
[XmlAttribute]
public T Item2 { get; set; }
}
public struct Meh
{
public int Prop { get; set; }
}
[Test]
public void XmlMehTest()
{
var meh = new Meh{Prop = 1};
var dummy = new Dummy<Meh>(meh);
using (var writer = new StringWriter())
{
var serializer = new XmlSerializer(dummy.GetType());
// System.InvalidOperationException : Cannot serialize member 'Meh2' of type Meh.
// XmlAttribute/XmlText cannot be used to encode complex types.
serializer.Serialize(writer, dummy);
Console.Write(writer.ToString());
}
}
[Test]
public void XmlDateTimeTest()
{
var dummy = new Dummy<DateTime>(DateTime.Now);
using (var writer = new StringWriter())
{
var serializer = new XmlSerializer(dummy.GetType());
serializer.Serialize(writer, dummy);
Console.Write(writer.ToString());
}
}
Please ignore that the struct is mutable, wrote it like that for a compact sample.
This is truly a first-world-developer-problem but I'm still curious :)
The documentation says:
You can assign the XmlAttributeAttribute only to public fields or public properties that return a value (or array of values) that can be mapped to one of the XML Schema definition language (XSD) simple types (including all built-in datatypes derived from the XSD anySimpleType type). The possible types include any that can be mapped to the XSD simple types, including Guid, Char, and enumerations.
So to do this, we should be able to create our own type definition for XSD,I guess we can do that.Because this documentation contains full explanation about it.But what we can't do is, we can't include our definition to this list.Initially XML Serializer uses these types to figure out your type's XSD type definition.You can use this attribute with DateTime because it's definition creating with this method and storing in a HashTable:
AddPrimitive(typeof(DateTime), "dateTime", "DateTime",
TypeFlags.XmlEncodingNotRequired |
TypeFlags.HasCustomFormatter |
TypeFlags.CanBeElementValue | **TypeFlags.CanBeAttributeValue**);
AddPrimitive method:
private static void AddPrimitive(Type type, string dataTypeName, string formatterName, TypeFlags flags)
{
XmlSchemaSimpleType dataType = new XmlSchemaSimpleType {
Name = dataTypeName
};
TypeDesc desc = new TypeDesc(type, true, dataType, formatterName, flags);
if (primitiveTypes[type] == null)
{
primitiveTypes.Add(type, desc);
}
primitiveDataTypes.Add(dataType, desc);
primitiveNames.Add(dataTypeName, "http://www.w3.org/2001/XMLSchema", desc);
}
And this definition calling from XmlReflectionImporter like this (which is generating the exception according to StackTrace):
this.GetTypeDesc(name, ns, TypeFlags.CanBeElementValue | TypeFlags.CanBeTextValue | TypeFlags.CanBeAttributeValue);
I guess most important thing is here TypeFlags.CanBeAttributeValue and I think it's specify that this type can be attibute value.So as a result maybe we can serialize custom structs as an XmlAttirube but we can't do it with standart XmlSerializer.Because as I said it's using this list to figure out XSD type definition.And it's an initial list and it's impossible to add new element to that list.
P.S. You might want take a look at here http://msdn.microsoft.com/en-us/library/8w07bk3h(v=vs.80).aspx

Separating a CSV file contents into an array

My CSV file is formatted like this
Object,Value,Attribute
Object,Value,Attribute
Object,Value,Attribute
etc.
Would I need 3 separate arrays? (I think that's the best way of doing this..) One for object which I can then feed into my chart.. one for value and the attribute. I don't know much about arrays yet.
You should create your own class that holds Object, Value and Attribute and store this in a list.
class SomeClass {
public string MyObject { get; set; }
public string MyValue { get; set; }
public string MyAttribute { get; set; }
}
private List<SomeClass> myList = new List<SomeClass>();
public void ReadCsv(){
using (var sr = new StreamReader("PathToCsvFile")) {
string currentLine;
while ((currentLine = sr.ReadLine()) != null) {
var elements = currentLine.Split(',');
myList.add(new SomeClass {
MyObject = elements[0],
MyValue = elements[1],
MyAttribute = elements[2]
});
}
}
}
I'd recommend one array of a class that holds object, value, and attribute, because then you don't need to worry about the complications of missing data, and changing all your arrays if you add more columns.
What you do with the fields depends on how you're going to use the data. You probably need an array of structs or objects, where each element in the array is a row and each member of the object/struct is a column.
Here's a very simple example of a struct, where you can hold your data:
struct MyStruct
{
string Column1;
string Column2;
//etc
}
And here's some code to populate it from the file:
List<MyStruct> rows = new List<MyStruct>;
s = reader.ReadLine();
while (s != null)
{
string s[] columns = SplitLine(s);
MyStruct row = new MyStruct();
row.Column1 = s[0];
row.Column2 = s[1];
rows.Add(row);
s = reader.ReadLine();
}
Notice the ambiguous function "SplitLine." Lots of ways to split a string up . Look here for the best ways to split the string into fields.
It really depends on what you want to do with the data. It seems you want to chart the data per category. If that is the case then the simplest way is to put each col in the same list.
1) read the csv file, per line
2) split the line based on comma
3) put data (in the same column) to the same list

XmlSerializer - How can I set a default when deserializing an enum?

I have a class that looks like this (heavily simplified):
public class Foo
{
public enum Value
{
ValueOne,
ValueTwo
}
[XmlAttribute]
public Value Bar { get; set; }
}
I'm receiving an XML file from an external source. Their documentation states that the Foo element will only ever have "ValueOne" or "ValueTwo" in the Bar attribute (they don't supply an XSD).
So, I deserialize it like this:
var serializer = new XmlSerializer(typeof(Foo));
var xml = "<Foo Bar=\"ValueTwo\" />";
var reader = new StringReader(xml);
var foo = (Foo)serializer.Deserialize(reader);
... and that all works.
However, last night, they sent me some XML looking like this instead, and my deserialization failed (as it should):<Foo Bar="" />
Is there a good way to defensively code around this? Ideally I'd like to say something like "default to ValueOne, if something goes wrong here". I don't want to throw away the whole XML file, because a single attribute was mangled.
you can set you enum like this , so this will set "" value of enum to Unknown = 0 somewhat like default value
public enum Foo
{
[XmlEnum(Name = "")]
Unknown =0,
[XmlEnum(Name = "Single")]
One,
[XmlEnum(Name = "Double")]
Two
}
more detail check : XmlEnumAttribute Class
You can manually parse enum value by creating another property with the same XmlAttribute name:
public enum Value
{
// Default for unknown value
Unknown,
ValueOne,
ValueTwo
}
[XmlIgnore]
public Value Bar { get; set; }
[XmlAttribute("Bar")]
public string _Bar
{
get { return this.Bar.ToString(); }
set { this.Bar = Enum.TryParse(value, out Value enumValue) ? enumValue : Value.Unknown; }
}
Usage the same as your before
var serializer = new XmlSerializer(typeof(Foo));
var xml = "<Foo Bar=\"invalid value\" />";
var reader = new StringReader(xml);
var foo = (Foo)serializer.Deserialize(reader);
Shoot down XmlSerializer..
Use LINQ2XML for this simple task
XElement doc=XElement.Load("yourStreamXML");
List<Foo> yourList=doc.Descendants("Foo")
.Select(x=>
new Foo
{
Bar=(Enum.GetNames(typeof(Value)).Contains(x.Attribute("Bar").Value))?((this.Value)x.Attribute("Bar")):(this.Value.ValueOne);
}
).ToList<Foo>();
So,I am basically doing this
if(Enum.GetNames(typeof(Value)).Contains(x.Attribute("Bar").Value))
//if the xml bar value is a valid enum
Bar=(this.Value)x.Attribute("Bar");
else//xml bar value is not a valid enum..so use the default enum i.eValueOne
Bar=this.Value.ValueOne;

Name Each Element in Array

I have an array whose indices map back to Enum values. I would like to use the C# Xml Serialization libraries to serialize this array. However, I would like each Xml node to say the name of the Enum type that it represents. How can I do this?
Example:
public class NumberOfEmployees
{
public enum EmployeeType { Lawyer, Doctor, Engineer };
public int[] NumEmployees { get; set; }
// Constructor initializes array to size of "EmployeeType"
public NumberOfEmployees()
{
int size = Enum.GetValues(typeof(EmployeeType)).Length;
this.NumEmployees = new int[size];
}
}
Main()
{
NumberOfEmployees numEmployees = new NumberOfEmployees();
// Add doctors, lawyers, engineers, etc...
// numEmployees.NumEmployees[(int)NumberOfEmployees.Lawyer] = 3;
// numEmployees.NumEmployees[(int)NumberOfEmployees.Doctor] = 2;
// numEmployees.NumEmployees[(int)NumberOfEmployees.Engineer] = 1;
// Serialize to "file"
FileStream fs = new FileStream(file, FileMode.OpenOrCreate);
XmlSerializer xml = new XmlSerializer(typeof(NumberOfEmployees));
xml.Serialize(fs, numEmployees);
fs.Close();
}
The end result is xml that looks something like this:
<NumEmployees>
<int>3</int>
<int>2</int>
<int>1</int>
</NumEmployees>
But what I want is:
<NumEmployees>
<Lawyer>3</Lawyer>
<Doctor>2</Doctor>
<Engineer>1</Engineer>
</NumEmployees>
I can not store each number separately -- it must be an array.
Thanks.
You can implement the IXmlSerializable interface to completely customize serialization with XmlSerializer. Something similar to the following:
public class NumberOfEmployees : IXmlSerializable
{
public int[] NumEmployees { get; set; }
// Constructor initializes array to size of "EmployeeType"
public NumberOfEmployees()
{
int size = Enum.GetValues(typeof(EmployeeType)).Length;
this.NumEmployees = new int[size];
}
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement();
NumEmployees[(int)EmployeeType.Lawyer] = int.Parse(reader.ReadElementString("Lawyer"));
NumEmployees[(int)EmployeeType.Doctor] = int.Parse(reader.ReadElementString("Doctor"));
NumEmployees[(int)EmployeeType.Engineer] = int.Parse(reader.ReadElementString("Engineer"));
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("Lawyer", NumEmployees[(int)EmployeeType.Lawyer].ToString());
writer.WriteElementString("Doctor", NumEmployees[(int)EmployeeType.Doctor].ToString());
writer.WriteElementString("Engineer", NumEmployees[(int)EmployeeType.Engineer].ToString());
}
}
After you've done all that it might seem pointless to still use XmlSerializer since your class is handling all the work of serialization. It still makes sense, however, if the NumberOfEmployees is part of a larger XML structure (which I assume it is).
Also note that the code doesn't do any validation, because this is a simple example. So all three array elements will be expected to exist when the class is serialized, and all three XML elements will be expected to exist when it is deserialized.
More info on the IXmlSerializable interface is available here:
http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx

Categories

Resources