Trying to convert a xml file to an object [closed] - c#

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I was wondering what your opinion about this would be. I'm trying to convert an xml file to a .net object. It's a xml file from the World of Warcraft armory. Here is an example.
<?xml version="1.0" encoding="UTF-8"?>
<baseStats>
<strength attack="48" base="48" block="-1" effective="58"/>
<agility armor="114" attack="-1" base="47" critHitPercent="4.27" effective="57"/>
<stamina base="70" effective="1186" health="11680" petBonus="-1"/>
<intellect base="198" critHitPercent="10.41" effective="1529" mana="22655" petBonus="-1"/>
<spirit base="190" effective="770" healthRegen="39" manaRegen="503"/>
<armor base="2150" effective="2150" percent="12.37" petBonus="-1"/>
</baseStats>
I've thought of 2 ways to convert this to an object and I'd like to hear your opinion about it. Ill show them.
class BaseStats{
public int StrengthAttack{get;set;}
public int StrengthBase{get;set;}
public int StrengthBlock{get;set;}
...
public int ArmorBase{get;set;}
public int ArmorEffective{get;set;}
...
}
or
class BaseStats{
public Strength Strength{get;set;}
public Armor Armor{get;set;}
public class Strength{
public int Attack{get;set;}
public int Base{get;set;}
public int Block{get;set;}
}
public class Armor{
public int Base{get;set;}
public int Effective{get;set;}
}
}
Do you see what I'm trying here. What would be your opinion about each of those ways. Or can you think of any others?

Classless Design using an Anonymous Type
Here's another way, with .NET 3.5, if you don't want to design an explicit class you can build the object dynamically as an anonymous type; one caveat being the object properties are read-only after being initialized.
Use the XML LINQ classes to query the XML content with.
using System;
using System.IO;
using System.Xml.Linq;
Load up the XML string and query it to create an object.
// 1. Load your string into a document.
XDocument xdoc = XDocument.Load(new StringReader(my_WoW_XML_String));
// 2. Create the anonymous type...
var statsObject = new
{
StrengthInfo = new
{
Attack = int.Parse(xdoc.Element("strength").Element("attack").Value),
Base = int.Parse(xdoc.Element("strength").Element("base").Value),
Block = int.Parse(xdoc.Element("strength").Element("block").Value),
Effective = int.Parse(xdoc.Element("strength").Element("effective").Value),
},
AgilityInfo = new
{
Armor = int.Parse(xdoc.Element("agility").Element("armor").Value),
Attack = int.Parse(xdoc.Element("agility").Element("attack").Value),
Base = int.Parse(xdoc.Element("agility").Element("base").Value),
CritHitPercent = int.Parse(xdoc.Element("agility").Element("critHitPercent").Value),
Effective = int.Parse(xdoc.Element("agility").Element("effective").Value),
}
// Do the same with <spirit> and <armor> elements, etc.
// Include only the properties you want from the XML.
}; // end anonymous object.
Use your anonymous object normally like so:
Console.Write("strength: attack={0}, effective={1}; armor agility={2}",
statsObject.StrengthInfo.Attack,
statsObject.StrengthInfo.Effective,
statsObject.AgilityInfo.Armor);
// Do whatever you want with the object version of WoW stats.
If you have multiple XML files to process with the same schema then just wrap all the above in a loop to process one at a time.

If you have the XSD schema (or can create one), I've used LinqToXsd to create some quick objects. The nice thing about LinqToXsd is that it creates methods you can use to easily parse XML into your objects.
Microsoft has released it as Open Source - and you can simply use a command-line call to pass in your .xsd and it will generate a .cs file with all of your classes.

class CElement
{
public int? attack { get; set; }
public int? base { get; set; }
public int? block { get; set; }
public int? effective { get; set; }
public int? petBonus { get; set; }
public int? mana { get; set; }
public int? healthRegen { get; set; }
public int? manaRegen { get; set; }
public double? critHitPercent { get; set; }
public double? percent { get; set; }
}
class CBaseStats
{
public CElement strength;
public CElement agility;
public CElement stamina;
public CElement intellect;
public CElement spirit;
public CElement armor;
}

If you got complete xml,
1. generate xsd from xml using the xsd.exe tool
2. generate class from the generated xsd using xsd.exe tool
http://msdn.microsoft.com/en-us/library/x6c1kb0s(VS.71).aspx

Related

Method to return a List<T> from a List<OwnStruct>, where List<T> then contains only one Property of all OwnStructs in List (C#) [duplicate]

This question already has answers here:
convert a list of objects from one type to another using lambda expression
(14 answers)
Closed 1 year ago.
I'm struggling with this a little.
I have a List<HeadStruc_Table> within my program.
The Class HeadStruct looks like following:
public partial class HeadStruct_Table : IComparable<HeadStruct_Table>
{
public string colName { get; set; }
public string colName_edit { get; set; }
public string alternativeNames { get; set; }
public int Table_ID { get; set; }
public bool colFound { get; set; }
public CheckBox cBox { get; set; }
I don't know how to create a method with parameters (List<HeadStruct_Table>, HeadStruct_Table.colName) that then returns a List<TypeOf(HeadStruct_Table.colName)> containing only the values of colName in this specific case.
Of course it should work for the bool and even CheckBox property as well.
As parameter HeadStruct_Table.colName doesn't work right now, as it is declared as just public and not public static, do i have to declare it as public static or is there any other chance to pass the specific property. Maybe by using a predicate?
That's the way it maybe could look like later?
public static IList<T> getList<T>(List<HeadStruct_Table> list, Func<HeadStruct_Table, T> getType)
{
var newList = new List<T>();
I just don't know how to get the special property and then, in the method, just read out those values. I wouldn't like to work with a string as parameter if it works without.
Anyone who has an idea?
That is my first question. I'm open for any advice to improve asking a question in here. Thank You.
LINQ's Enumerable.Select method already does what you want:
var newList = list.Select(x => x.colName).ToList();

Can't access data through multiple class layers [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
For my Project I have to build a data structure to represent an xml file.
My Idea was to use multiple classes representing the different xml layers.
My problem now is how can I change data in the lowest layer from the uppermost one.
In my code example are three calls 2 work 1 doesn't. Why doesn't the last one work?
Is there another way of organizing the data?
public class option
{
public string optionID;
public void setoptionID(string text)
{
optionID = text;
}
}
public class module
{
public option[] opt = new option[1];
private string moduleID;
public void setmoduleID(string text)
{
moduleID = text;
}
}
public class catalogitem
{
public module[] modul = new module[1];
private string ShortName;
public void setShortName(string text)
{
ShortName = text;
}
}
public class Katalog
{
private catalogitem[] items = new catalogitem[1];
public Katalog()
{
}
public void setcatalogitems()
{
items[0].setShortName("asdf"); //works
catalogitem.modul[0].setmoduleID("5"); //works
items[0].modul[0].setmoduleID("5"); //doesn't work
}
}
For me, none of the three statement work.
You have to initialize the objects itself, not only the array.
items[0] = new catalogitem();
items[0].setShortName("asdf");
items[0].modul[0] = new module();
items[0].modul[0].setmoduleID("5");
But I would also suggest you, to use properties instead of setShortName() / setmoduleID() in your classes and methods for adding and initializing sub items.
Of course it depends on the specification / your intention (that I don't know), but here is a possible way to implement for example
CatalogItem. You can read/write ShortName and enumerate all existing modules. I would take a list, not an array. Modules can be
only added one by one and you can check them before really adding them:
public class CatalogItem
{
private readonly List<Module> mModuls;
public IEnumerable<Module> Moduls
{
get { return mModuls; }
}
public string ShortName { get; set; }
public CatalogItem()
{
mModuls = new List<Module>();
}
public void AddModule(Module module)
{
// Add a check that module is assigned.
mModuls.Add(module);
}
}
Some suggestions
Use properties instead of Set Methods
Write only properties are not good by design
if you need only one object of a type, create the object instead of array with 1 object
follow naming conventions C# Coding Conventions and Naming Guidelines
Your somewhat updated code is below.
public class Option
{
public string OptionID { get; set;}
}
public class Module
{
// if you need only one Option, why not create one public Option object, instead of an Array
public Option Option = new Option();
public string ModuleID { get; set; }
}
public class CatalogItem
{
public Module Module = new Module();
public string ShortName { get; set; }
}
public class Katalog
{
private List<CatalogItem> items = new List<CatalogItem>();
public Katalog()
{
for(int i = 0; i < 10; i++)
items.Add(new CatalogItem());
}
public void SetCatalogItem()
{
foreach(CatalogItem ci in items)
{
ci.ShortName = "asdf";
ci.Module.ModuleID = "5";
}
}
}
Glad to help! Please remember to accept the answer if you found it helpful.

Using a Variable as List Type in C#

I am currently using a list to handle a JSON string which works fine for one instance of this, as can be seen below. What I want to do is make these methods that handle the conversion completely generic so I can use them for multiple JSON strings.
This is a snippet of my current code as it stands.
public class GetPerson
{
public string fooName { get; set; }
public string fooAddress { get; set; }
public string fooPosition { get; set; }
}
public class GetPosition
{
public string fooTitle { get; set; }
public string fooDepartment { get; set; }
public string fooSalary { get; set; }
}
private static List<GetPerson> ConvertToList(string jsonString)
{
List< listJson = new List<JsonObject>();
listJson = (List<GetPerson>)JsonConvert.DeserializeObject<List<GetPerson>>(jsonString);
return listJson;
}
This is just a quick sample but the List<GetPerson> is what I need to be generic so it can be reused, because as it stands the GetPosition will obviously not work with this, as I would want to be able to iterate through my code changing the type accordingly.
Is there a way I can assign a variable as a type? I saw another question about this but it didn't go into detail. Or is there another way that this could be achieved?
Thanks in advance.
Very Simple. You just have to make ConvertToList() generic and pass the desired class as Type Paramter in ConvertToList()
private static List<T> ConvertToList<T>(string jsonString)
{
var listJson = new List<JsonObject>();
listJson = (List<T>)JsonConvert.DeserializeObject<List<T>>(jsonString);
return listJson;
}
var personList = ConvertToList<GetPerson>(jsonString);
var positionList = ConvertToList<GetPosition>(jsonString);
You can use Generics to help make the ConvertToList function reusable for different types
private static List<T> ConvertToList<T>(string jsonString)
{
return (List<T>)JsonConverty.DeserializeObject<List<T>>(jsonString();
}
You can now call it using both GetPerson and GetPosition as the generic type.
var listOfPeople = ConvertToList<GetPerson>(personJson);
var listOfPositions = ConvertToList<GetPosition>(positionJson);
You can read more about Generics on MSDN.
Also, if all that you want to do is to [de]serialize JSON, you might want to consider a third-party library for that like JSON.net, Jil or ServiceStack.Text, all of which have built in functions to do what you are trying to do.

StructureMap List of Non primitive types

I am trying to figure out how to setup StructureMap (using an XML Configuration file). One class has a constructor with a list containing instances of a 2nd class:
public interface ITestDocType { }
class TestDocType : ITestDocType
{
public List<AttributeRef> AttrRefs { get; set; }
public TestDocType(List<AttributeRef> attrRefs)
{
AttrRefs = attrRefs;
}
}
public class AttributeRef
{
public AttributeRef(string name, string xpath, string value)
{
Name = name;
Xpath = xpath;
Value = value;
}
public string Name { get; set; }
public string Xpath { get; set; }
public string Value { get; set; }
}
I was hoping to be able to inline the instances of AttributeRef in my configuration file, but not entirely sure how its done (or if its possible).
<DefaultInstance PluginType="ITestDocType" PluggedType="TestDocType">
<attrRefs>
// Would like to specify one to many AttributeRef instances inline here
</attrRefs>
</DefaultInstance>
Ok.. I figured it out, and it was described pretty nicely in the documentation.. I just needed to read it a few times over to fully understand.
<DefaultInstance PluginType="yyy"
PluggedType="yyy">
<attrRefs>
<Child>
<DefaultInstance PluginType="xxx"
PluggedType="xxx"
name="id" x
path="/item/#idd"
attrValue="none">
</DefaultInstance>
</Child>
</attrRefs>
</DefaultInstance>
As you can see, "attrRefs" is the name of the parameter in the constructor that takes List, and for each element you want to add to that list, wrap the DefaultInstance element inside a "Child" element.

Serializing Name/Value Pairs in a Custom Object via Web Service

This is a very complicated question concerning how to serialize data via a web service call, when the data is not-strongly typed. I'll try to lay it out as best possible.
Sample Storage Object:
[Serializable]
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public List<NameValuePairs> OtherInfo { get; set; }
}
[Serializable]
public class NameValuePairs {
public string Name { get; set; }
public string Value { get; set; }
}
Sample Use:
[WebMethod]
public List<StorageObject> GetStorageObjects() {
List<StorageObject> o = new List<StorageObject>() {
new StorageObject() {
Name = "Matthew",
Birthday = "Jan 1st, 2008",
OtherInfo = new List<NameValuePairs>() {
new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
}
},
new StorageObject() {
Name = "Joe",
Birthday = "Jan 10th, 2008",
OtherInfo = new List<NameValuePairs>() {
new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
}
}
};
return o;
}
Return Value from Web Service:
<StorageObject>
<Name>Matthew</Name>
<Birthday>Jan 1st, 2008</Birthday>
<OtherInfo>
<NameValuePairs>
<Name>Hobbies</Name>
<Value>Programming</Value>
</NameValuePairs>
<NameValuePairs>
<Name>Website</Name>
<Value>Stackoverflow.com</Value>
</NameValuePairs>
</OtherInfo>
</StorageObject>
What I want:
<OtherInfo>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</OtherInfo>
The Reason & Other Stuff:
First, I'm sorry for the length of the post, but I wanted to give reproducible code as well.
I want it in this format, because I'm consuming the web services from PHP. I want to easily go:
// THIS IS IMPORANT
In PHP => "$Result["StorageObject"]["OtherInfo"]["Hobbies"]".
If it's in the other format, then there would be no way for me to accomplish that, at all. Additionally, in C# if I am consuming the service, I would also like to be able to do the following:
// THIS IS IMPORANT
In C# => var m = ServiceResult[0].OtherInfo["Hobbies"];
Unfortunately, I'm not sure how to accomplish this. I was able to get it this way, by building a custom Dictionary that implemented IXmlSerializer (see StackOverflow: IXmlSerializer Dictionary), however, it blew the WSDL schema out of the water. It's also much too complicated, and produced horrible results in my WinFormsTester application!
Is there any way to accomplish this ? What type of objects do I need to create ? Is there any way to do this /other than by making a strongly typed collection/ ? Obviously, if I make it strongly typed like this:
public class OtherInfo {
public string Hobbies { get; set; }
public string FavoriteWebsite { get; set; }
}
Then it would work perfectly, I would have no WSDL issues, I would be able to easily access it from PHP, and C# (.OtherInfo.Hobbies).
However, I would completely lose the point of NVP's, in that I would have to know in advance what the list is, and it would be unchangeable.. say, from a Database.
Thanks everyone!! I hope we're able to come up with some sort of solution to this. Here's are the requirements again:
WSDL schema should not break
Name value pairs (NVP's) should be serialized into attribute format
Should be easy to access NVP's in PHP by name ["Hobbies"]
Should be easy to access in C# (and be compatible with it's Proxy generator)
Be easily serializable
Not require me to strongly type the data
Now, I am /completely/ open to input on a better/different way to do this. I'm storing some relatively "static" information (like Name), and a bunch of pieces of data. If there's a better way, I'd love to hear it.
This is like dynamic properties for a object.
C# is not quite a dynamic language unlike javascript or maybe PHP can parse the object properties on the fly. The following two methods are what I can think of. The second one might fit into your requirements.
The KISS Way
The Keep It Simple Stupid way
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public List<string> OtherInfo { get; set; }
}
You can have name value pairs which is separated by '|'
OtherInfo = {"Hobbies|Programming", "Website|Stackoverflow.com"}
Serialized forms
<StorageObject>
<Name>Matthew</Name>
<Birthday>Jan 1st, 2008</Birthday>
<OtherInfo>
<string>Hobbies|Programming</string>
<string>Website|Stackoverflow.com</string>
</OtherInfo>
</StorageObject>
The Dynamic Way in C#
Make the name value pair part become an XML element so that you can build it dynamically.
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public XElement OtherInfo { get; set; } // XmlElement for dot net 2
}
You can easily build up OtherInfo object as element centric
e.g.
XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..Hobbies xelement & text value..);
OtherInfo.Add( ..WebSite xelement & text value..);
The serialized form will be
<OtherInfo>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</OtherInfo>
or build it as attribute centric
XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..nvp xattribute Hobbies & value..);
OtherInfo.Add( ..nvp xattribute WebSite & value..);
<OtherInfo>
<nvp n="Hobbies" v="Programming" />
<nvp n="Website" v="Stackoverflow.com" />
</OtherInfo>
For any dynamic language, it can access to the properties directly.
For the rest, they can access the value by read the XML. Reading XML is well supported by most of framework.
This is what I've settled on.
Class Structure:
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
[XmlAnyElement("Info")] // this prevents double-nodes in the XML
public XElement OtherInfo { get; set; }
}
Usage:
StorageObject o = new StorageObject();
o.OtherInfo.Add(new XElement("Hobbies","Programming");
o.OtherInfo.Add(new XElement("Website","Stackoverflow.com");
Output:
<Info>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</Info>
I would like to thank everyone for their assistance, I really appreciate the help and ideas.
As a completely different take on this, why not think about doing it completely differently. Have one web service method to return the serialized storage object, minus the OtherInfo and another method to return the list of properties (keys) for OtherInfo, and a third to return the list of values for any key. Granted, it will take more round trips to the web service if you want all of the data, but the solution will be much simpler and more flexible.
[Serializable]
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
[Nonserializable]
public Dictionary<string,List<string>> OtherInfo { get; set; }
}
[WebMethod]
public List<StorageObject> GetStorageObjects() {
// returns list of storage objects from persistent storage or cache
}
[WebMethod]
public List<string> GetStorageObjectAttributes( string name )
{
// find storage object, sObj
return sObj.Keys.ToList();
}
[WebMethod]
public List<string> GetStorageObjectAtributeValues( sting name, string attribute )
{
// find storage object, sObj
return sObj[attribute];
}
Have a look into the System.Xml.Serialization.XmlSerializerAssemblyAttribute attribute. This lets you specify a custom class-level serializer. You'll be able to spit out whatever XML you like.
A quick way to get up to speed on these is to use sgen.exe to generate one and have a peek at it with Reflector.
-Oisin
I'm not sure this would solve your problem (it would in C#, but maybe not in PHP), but try using Dictionary<string,List<string>> OtherInfo instead of List<NameValuePairs>. Then "Hobbies" and "Websites" would be your keys and the values would be the list of hobbies or web sites. I'm not sure how it would serialize, though.
You would be able to reference the lists of hobbies as:
List<string> hobbies = storageObject.OtherInfo["Hobbies"];
[EDIT] See here for a generic XML serializable dictionary. This derived class is the one you would need to use instead of generic Dictionary.

Categories