I have products in a class that I want to save in a XML file, the products is devided into categoris and I would like to save my products in a XML file formatet like below examlple. Products for category 1 writes under category one and products for category 2 under category two.
Hwo can I write a method that dose that?
Thanks in advance.
<Categories>
<Category ID="1">
<CategoryName>Categoriname1</CategoryName>
<Description>drinks, coffees, beers, ales</Description>
<Products>
<Product>
<ProductName>Coffe</ProductName>
<QuantityPerUnit>15 boxes x 60 bags</QuantityPerUnit>
<UnitPrice>25</UnitPrice>
<UnitsInStock>3</UnitsInStock>
<UnitsOnOrder>0</UnitsOnOrder>
</Product>
<Product>
<ProductName>Chang</ProductName>
<QuantityPerUnit>24 - 12 oz bottles</QuantityPerUnit>
<UnitPrice>19</UnitPrice>
<UnitsInStock>17</UnitsInStock>
<UnitsOnOrder>40</UnitsOnOrder>
</Product>
</Products>
</Category>
<Category ID="2">
<CategoryName>Condiments</CategoryName>
<Description>spreads, and seasonings</Description>
<Products>
<Product>
<ProductName>Productname</ProductName>
You can use LINQ to XML:
http://www.hookedonlinq.com/LINQtoXML5MinuteOverview.ashx
You can use LINQ to XML.
your example would start off like...
var root = new XElement("Categories",
new XElement("Category",
new XAttribute("ID",1),
new XElement("CategoryName", "Categoriname1")
)
);
Your intellisense should help you get the rest
One option is to use LINQ to XML. Assume you have these classes (where I have removed some properties to simplify the example):
class Category {
public Int32 Id { get; set; }
public String Name { get; set; }
public IEnumerable<Product> Products { get; set; }
}
public class Product {
public String Name { get; set; }
}
You can create some test data:
var categories = new[] {
new Category {
Id = 1,
Name = "Category 1",
Products = new[] {
new Product { Name = "Coffee" },
new Product { Name = "Chang" }
}
},
new Category {
Id = 2,
Name = "Condiments",
Products = new[] {
new Product { Name = "Product 1" }
}
}
};
You can then create an XDocument from the test data:
var xmlDocument = new XDocument(
new XElement(
"Categories",
categories.Select(
c => new XElement(
"Category",
new XAttribute("ID", c.Id),
new XElement("CategoryName", c.Name),
new XElement("Products",
c.Products.Select(
p => new XElement(
"Product",
new XElement("ProductName", p.Name)
)
)
)
)
)
)
);
To save it to a file you can use the Save method:
xmlDocument.Save("Categories.xml");
You need to serialize those classes. Create classes marked with Serializable attribute and use XmlSerializer
example:
http://www.dotnetjohn.com/articles.aspx?articleid=173
You could use the XmlSerializer class or the XmlDocument class or the XDocument class or even the XmlTextWriter class.
You may also like to simply serialise the object(s) that you have. You may write a method like this:
public static bool WriteToXMLFile(string fullFileNameWithPath, Object obj, Type ObjectType)
{
TextWriter xr = null;
try
{
XmlSerializer ser = new XmlSerializer(ObjectType);
xr = new StreamWriter(fullFileNameWithPath);
ser.Serialize(xr, obj);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if(xr != null)
xr.Close();
}
return true;
}
Related
I am trying to append my list to an XML file.
I have my c# class PasswordSettings which contains some properties:
public class PasswordSettings {
public string customerRef { get; set; }
public string node { get; set; }
public string name { get; set; }
public string login { get; set; }
public string password { get; set; }
public string fileType { get; set; }
}
I have a list of PasswordSettings like this:
public List<PasswordSettings> Logins = new List<PasswordSettings>();
I now add elements to my object and add the object to my list:
PasswordSettings settings = new PasswordSettings();
settings.customerRef = "abc";
settings.name = "test";
Logins.add(settings);
Now I want to add this list to an XML file so I end up with something like:
<PasswordSettings>
<Logins>
<customerRef>abc</customerRef>
<name>test</name>
</Logins>
</PasswordSettings>
And if I want to add another login, it will append to the XML file, i.e. not replace or overwrite anything, so a new <Logins>
I have tried multiple methods and I either get null pointers or nothings gets written. I suppose the nullpointer could be because the XML file is empty, but I just want it to add this list as an XML structure.
here is a solution to create xml or add a new record, so after you could adapt following what you want:
PasswordSettings settings = new PasswordSettings();
settings.customerRef = "abc";
settings.name = "test";
Logins.Add(settings);
settings = new PasswordSettings();
settings.customerRef = "def";
settings.name = "test1";
Logins.Add(settings);
foreach (var login in Logins)
{
if (!File.Exists(#"e:\Test.xml"))
{
XDocument doc =
new XDocument(
new XElement("PasswordSettings",
new XElement("Logins",
new XElement("customerRef", login.customerRef),
new XElement("name", login.name)
)
)
);
doc.Save(#"e:\Test.xml");
}
else
{
XDocument doc = XDocument.Load(#"e:\Test.xml");
XElement root = doc.Element("PasswordSettings");
IEnumerable<XElement> rows = root.Descendants("Logins");
XElement firstRow = rows.First();
firstRow.AddBeforeSelf(
new XElement("Logins",
new XElement("customerRef", login.customerRef),
new XElement("name", login.name)));
doc.Save(#"e:\Test.xml");
}
}
}
xml output:
<?xml version="1.0" encoding="utf-8"?>
<PasswordSettings>
<Logins>
<customerRef>def</customerRef>
<name>test1</name>
</Logins>
<Logins>
<customerRef>abc</customerRef>
<name>test</name>
</Logins>
</PasswordSettings>
here i add at the beginning of file, if you want to add at the end of file, just do:
XElement firstRow = rows.Last();
firstRow.AddAfterSelf(
new XElement("Logins",
new XElement("customerRef", login.customerRef),
new XElement("name", login.name)));
output:
<?xml version="1.0" encoding="utf-8"?>
<PasswordSettings>
<Logins>
<customerRef>abc</customerRef>
<name>test</name>
</Logins>
<Logins>
<customerRef>def</customerRef>
<name>test1</name>
</Logins>
</PasswordSettings>
I've been using the answer here
binding xml elements to model in MVC4
to bind an XML file to a model.
This works fine when the XML file has one element. I need to handle the case when there are many elements. I'd like all of those elements to go into a List.
Here's an example file:
<root>
<item>
<firstname>Tom</firstname>
</lastname>Jones</lastname>
<item>
<item>
<firstname>Jane</firstname>
</lastname>Doe</lastname>
</item>
</root>
MyXMLElements class looks like:
[Serializable]
[XmlRoot("item")]
public class MyXMLElements
{
public string first name {get;set;}
public string lastname {get;set;}
}
How I do I create a List<MyXMLElements>?
I think the easiest way is to use the XmlSerializer:
XmlSerializer serializer = new XmlSerializer(typeof(List<MyClass>));
using(FileStream stream = File.OpenWrite("filename"))
{
List<MyClass> list = new List<MyClass>();
serializer.Serialize(stream, list);
}
using(FileStream stream = File.OpenRead("filename"))
{
List<MyClass> dezerializedList = (List<MyClass>)serializer.Deserialize(stream);
}
You can achieve it this way (I'm reading XML from a file, but you can get it from other source):
XDocument xDoc = XDocument.Load(#"C:\new.xml");
List<MyXMLElements> list = (from xEle in xDoc.Descendants("item")
select new MyXMLElements() { firstname = xEle.Element("firstname").Value, lastname = xEle.Element("lastname").Value }).ToList();
You don't need XmlRoot in this case.
XElement xelement = XElement.Load("..\\..\\XML.xml");
IEnumerable<XElement> employees = xelement.Elements();
Console.WriteLine("List of all Employee Names :");
foreach (var employee in employees)
{
Console.WriteLine(employee.Element("firstname").Value + employee.Element("lastname").Value );
}
A solution is given below:
The class should look like this:
[Serializable]
[XmlType(TypeName = "item")]
public class MyXMLElements
{
public string firstname {get;set;}
public string lastname {get;set;}
}
The deserialization code is below:
using (var rdr = System.Xml.XmlReader.Create(#"input.xml"))
{
var root=new System.Xml.Serialization.XmlRootAttribute("root");
var ser = new System.Xml.Serialization.XmlSerializer(typeof(List<MyXMLElements>),root);
var list=(List<MyXMLElements>)ser.Deserialize(rdr);
}
I'm just trying to understand Linq and I am trying to do something that seems very simple, but I can't get it to output the way I would like. I have been stuck on this for days trying various different methods I just can't get it right.
So I have a class EarObs, it has members: eventID, icaoId, frm, sta, db.
I'm trying to build an XML document from a List. I want the XML document to look like so:
<EarObs EventId = "123456789">
<icao icaoID = "0001">
<frm frm = "01">
<sta sta = "00">
<db>87</db>
<hz>99</hz>
</sta>
<sta station = "01">
<db>79</db>
<hz>99</hz>
</sta>
</frm>
<frm frm = "02">
................
</frm>
</icao>
</EarObs>
And this would continue all the way down keeping the same order if there was more than one frame or more than one code etc.
So this is what I have been trying most recently but it still does not output they way I would like, Obs get repeated and I do not know where I am going wrong.
string eventGUID = "eventGUID";
List<EarObs> frameObsList = new List<EarObs>();
for (int frm = 2; frm > 0; frm--)
{
for (int sta = 5; sta > 0; sta--)
{
frameObsList.Add(new EarObs("KAPF", eventGUID, frm, sta, 85 + sta, 99 + sta));
cnt++;
}
}
String eventID = obsList.First().EventGUID;
List<EarObs> distinctApts =
obsList
.GroupBy(p => p.IcaoId)
.Select(g => g.First())
.ToList();
XElement xElement = new XElement("EarObs", new XAttribute("eventID", eventID),
from ea in distinctApts
orderby ea.IcaoId
select new XElement("icao", new XAttribute("code", ea.IcaoId),
from eb in obsList
where ea.IcaoId == eb.IcaoId
orderby eb.Frm
select new XElement("frm", new XAttribute("frm", eb.Frm),
from ec in obsList
where eb.Frm == ec.Frm
orderby ec.Sta
select new XElement("sta", new XAttribute("sta", ec.Sta),
new XElement("db", ec.Db),
new XElement("hz", ec.Hz)))));
Using this code I get an xml document that repeats the frame once for each station. This is not correct. I feel like this is easily done sequentially, but I'm trying to learn and this seems just so simple that I should be able to do it in Linq. I need each element in the List to only be represented in the XML document once. How do I go about this?
I would also like to expand it so that it can handle multiple eventId's as well, but that is not as important as getting the XML structure right. Any help would be much appreciated, I haven't been able to find too many example of creating an XML including the filtering of the elements using linq, most examples seem to have the List all ready structured before they create the XML.
Since you have a custom class, EarObs why not define Xml attributes to your object and serialize the object using the XmlSerlizer class? This way, you can continue use Linq on your objects, and also output your objects.
e.g. Below is a team, with players on it.
[XmlRoot("root")]
public class Team
{
private List<Player> players = new List<Player>();
[XmlElement("player")]
public List<Player> Players { get { return this.players; } set { this.players = value; } }
// serializer requires a parameterless constructor class
public Team() { }
}
public class Player
{
private List<int> verticalLeaps = new List<int>();
[XmlElement]
public string FirstName { get; set; }
[XmlElement]
public string LastName { get; set; }
[XmlElement]
public List<int> vertLeap { get { return this.verticalLeaps; } set { this.verticalLeaps = value; } }
// serializer requires a parameterless constructor class
public Player() { }
}
Once I create a team, with some players on it, I just have to do:
Team myTeamData = new Team();
// add some players on it.
XmlSerializer deserializer = new XmlSerializer(typeof(Team));
using (TextReader textReader = new StreamReader(#"C:\temp\temp.txt"))
{
myTeamData = (Team)deserializer.Deserialize(textReader);
textReader.Close();
}
The output will look like this:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<player>
<FirstName>dwight</FirstName>
<LastName>howard</LastName>
<vertLeap>1</vertLeap>
<vertLeap>2</vertLeap>
<vertLeap>3</vertLeap>
</player>
<player>
<FirstName>dwight</FirstName>
<LastName>howard</LastName>
<vertLeap>1</vertLeap>
</player>
</root>
The easiest way is to create a set of classes to handle the serialization like so;
public class sta
{
public int db { get; set; }
public int hz { get; set; }
[XmlAttribute()]
public string station { get; set; }
}
public class frm
{
[XmlAttribute("frm")]
public string frmID { get; set; }
[XmlElement("sta")]
public List<sta> stas { get; set; }
}
public class icao
{
[XmlAttribute]
public string icaoID { get; set; }
[XmlElement("frm")]
public List<frm> frms { get; set; }
}
public class EarObs
{
[XmlAttribute]
public string EventId { get; set; }
[XmlElement("icao")]
public List<icao> icaos { get; set; }
}
and you can use the xml serializer to serialize/deserialize. The following serializes to the structure identical to what you have;
XmlSerializer serializer = new XmlSerializer(typeof(EarObs));
EarObs obs = new EarObs() { EventId = "123456789" };
obs.icaos = new List<icao>();
obs.icaos.Add(new icao() { icaoID = "0001" });
obs.icaos[0].frms = new List<frm>();
obs.icaos[0].frms.Add(new frm() { frmID = "01" });
obs.icaos[0].frms[0].stas = new List<sta>();
obs.icaos[0].frms[0].stas.Add(new sta() { station = "00", db = 87, hz = 99 });
obs.icaos[0].frms[0].stas.Add(new sta() { station = "01", db = 79, hz = 99 });
obs.icaos[0].frms.Add(new frm() { frmID = "02" });
using (StringWriter s = new StringWriter())
{
serializer.Serialize(s, obs);
string test = s.ToString();
}
Outputs;
<?xml version="1.0" encoding="utf-16"?>
<EarObs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" EventId="123456789">
<icao icaoID="0001">
<frm frm="01">
<sta station="00">
<db>87</db>
<hz>99</hz>
</sta>
<sta station="01">
<db>79</db>
<hz>99</hz>
</sta>
</frm>
<frm frm="02" />
</icao>
</EarObs>
Now, while this seems like a lot of trouble to go to, it's possible to use the xsd.exe tool (comes with the framework I believe), to automatically create a set of classes that match any given xml file, although it does use an intermediary xsd file (painless though). You can find out how here; How to generate .NET 4.0 classes from xsd?
I currently have a XML file format that goes something like this (whitespace and ellipses added for readability):
<root>
<Module> //Start with list of Modules
<ModuleParams>
</ModuleParams>
</Module>
...
<DetectLine> //Now a list of DetectLines
<DetectLineParams>
</DetectLineParams>
<Channels> //List of Channels embedded in each DetectLine
<Channel>
<ChannelParams>
</ChannelParams>
</Channel>
...
</Channels>
</DetectLine>
...
</root>
Classes structured as follows:
public class Module
{
public ModuleParams { get; set; }
}
public class DetectLine
{
public DetectLineParams { get; set; }
public List<Channel> Channels { get; set; }
}
public class Channel
{
public ChannelParams { get; set; }
}
The list of Modules and DetectLines are easy to parse with Linq to XML. However, parsing the Channel list for each DetectLine is not as apparent to me. Can this even be done with Linq to XML? I would prefer not having to restructure things to work with a XMLSerializer.
Initial Code (openXML is a OpenFileDialog. Already checked for good filename):
List<Module> myModules;
List<DetectLine> myDetectLines;
XDocument config = XDocument.Load(openXML.FileName);
myModules =
(from myElements in config.Descendants("Module")
select new Module()
{
ModuleParam1 = (string)myElements.Element("ModuleParam1"),
ModuleParam2 = (string)myElements.Element("ModuleParam2"),
...
}).ToList<Module>();
myDetectLines =
(from myElements in config.Descendants("DetectLine")
select new DetectLine()
{
DetectLineParam1 = (string)myElements.Element("ModuleParam1"),
DetectLineParam2 = (string)myElements.Element("ModuleParam2"),
...
// ?? Add Channels list here somehow...
}).ToList<DetectLine>();
With
XElement detectLine = XElement.Parse(#"<DetectLine>
<DetectLineParams>
</DetectLineParams>
<Channels>
<Channel>
<ChannelParams>
</ChannelParams>
</Channel>
...
</Channels>
</DetectLine>
");
you can do e.g.
DetectLine dl1 = new DetectLine() {
DetectLineParams = ...,
Channels = (from channel in detectLine.Element("Channels").Element("Channel")
select new Channel() {
ChannelParams = new ChannelParams() { ... = channel.Element("ChannelParams").Value }
}).ToList();
We really need to see more of the concrete C# class code to spell out how to set up the complete query.
[edit]
To fit in with the code you have now posted:
myDetectLines =
(from myElements in config.Descendants("DetectLine")
select new DetectLine()
{
DetectLineParam1 = (string)myElements.Element("ModuleParam1"),
DetectLineParam2 = (string)myElements.Element("ModuleParam2"),
...
Channels = (from channel in myElements.Element("Channels").Element("Channel")
select new Channel() {
ChannelParams = new ChannelParams() { ... = channel.Element("ChannelParams").Value }
}).ToList();
}).ToList<DetectLine>();
I have a simple xml doc I am reading, a sample is here:
<people>
<person>
<name>joe</name>
<age>21</age>
<contact>
<phone-nums>
<phone-num>
<number>123-4567</number>
<type>home</type>
</phone-num>
<phone-num>
<number>123-4567</number>
<type>office</type>
</phone-num>
</phone-nums>
</contact>
</person>
</people>
I read it in using HttpContent.ReadAsXElement() and then use Linq to create objects. My simple objects look something like this:
public class PeopleList : List<Person> { }
public class Person
{
public string name;
public int age;
public Contact contact;
}
public class Contact
{
public PhoneList phones;
}
public class PhoneList : List<Phone>{}
public class Phone
{
public string number;
public string type;
}
Ok, so now I have my class that reads it all in which is where I am getting hung up (it's an extension method in my code):
public PeopleList ReadAsPeopleList(this HttpContent content)
{
var people = content.ReadAsXElement();
var personQuery = from p in people.Elements("person")
select new Person()
{
name = p.Element("name").ValueOrDefault(),
age = p.Element("age").ValueOrDefault(),
contact = (from c in p.Elements("contact")
select new Contact()
{
//I don't know how to select a new list of phones into a contact here
}
};
PeopleList l = new PeopleList();
l.AddRange(personQuery);
return l;
}
I'm having trouble creating the contact type with the composite phone number list. Any help would be appreciated.
Note: I rewrote a simplified version of all of this here so
To get the collection of 'Phone' that needs to go in the contact, you could use this:
c.Elements("phone-num").Select(phone => new Phone()
{
number = phone.Element("number").Value,
type = phone.Element("type").Value
});
so you want
select new Contact()
{
PhoneList = c.Elements("phone-num").Select(phone => new Phone()
{
number = phone.Element("number").Value,
type = phone.Element("type").Value
})
}
This answer will be a bit skewed from your actual question, but may provide some direction to your eventual solution.
Consider using a List<T> for your collections rather than creating a BusinessObjectCollection : List<T>. Here's a good SO read that may be of interest: List or BusinessObjectCollection?
With that being said, this is a somewhat tweaked version of your classes; I've used properties instead of fields as well. And finally, since I've not worked with HTTPContext much, I thew together an example using a basic string. The method presented here should be easy enough to convert into an extension method for HTTPContext, though:
public static IEnumerable<Person> ReadAsPeopleList( string xml )
{
var doc = XDocument.Parse( xml );
var people = doc.Root.Elements( "person" )
.Select( x => new Person
{
Name = x.Element( "name" ).Value,
Age = int.Parse( x.Element( "age" ).Value ),
Contact = new Contact
{
Phones = x.Descendants( "phone-num" )
.Select( p => new Phone
{
Number = p.Element( "number" ).Value,
Type = p.Element( "type" ).Value
} )
}
}
);
return people;
}
private static string MyXml = #"
<people><person><name>joe</name><age>21</age><contact><phone-nums>
<phone-num><number>123-4567</number><type>home</type></phone-num>
<phone-num><number>123-4567</number><type>office</type></phone-num>
</phone-nums></contact></person><person><name>bill</name><age>30</age>
<contact><phone-nums><phone-num><number>123-4567</number><type>home</type>
</phone-num><phone-num><number>123-4567</number><type>office</type>
</phone-num></phone-nums></contact></person></people>";