XML Serialization - serialize selected listbox items - c#

I'm having issues correctly serializing selected data from a c# winforms listbox into an XML file.
// save xml config
SessionConfig config = new SessionConfig();
foreach(String listitem in mylistbox.SelectedItems)
{
config.myItems = listitem;
}
config.Serialize(config);
and here's the SessionConfig class
public class SessionConfig
{
// mylistbox
public string myItems { get; set; }
public void Serialize(SessionConfig details)
{
XmlSerializer serializer = new XmlSerializer(typeof(SessionConfig));
using (TextWriter writer = new StreamWriter(string.Format("{0}\\config.xml", Application.StartupPath)))
{
serializer.Serialize(writer, details);
}
}
}
This will output an XML file like this:
<?xml version="1.0" encoding="utf-8"?>
<SessionConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<myItems>Item</myItems>
</SessionConfig>
What I'm trying to do is serialize all selected items, not just one item. And I would like to put each item in its own <Item> tag under the parent <myItems> node...like this:
<?xml version="1.0" encoding="utf-8"?>
<SessionConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<myItems>
<Item>Item</Item>
</myItems>
</SessionConfig>
I then want to be able to use the XML file to grammatically set the listbox selected items, but I'm not sure how I should go by looping through the node values.
This is what I have so far to read the values from my config file:
XmlDocument xml = new XmlDocument();
xml.Load(string.Format("{0}\\config.xml", Application.StartupPath));
XmlNode anode = xml.SelectSingleNode("/SessionConfig");
if (anode != null)
{
if (anode["MyItems"] != null)
}

The myItems property in the SessionConfig class needs to be a list. It's currently just a string and you'll only ever get the last value from the listbox since you overwrite the previous value each time at the line config.myItems = listitem;
Using a structure like this should get you the structure you're looking for:
public Item MyItems { get; set; }
[CollectionDataContract(ItemName = "Item")]
public class Item : List<string>{}
Then instead of using config.myItems = listitem; to add each item - you would use config.MyItems.Add(listItem)

Related

How to remove empty namespace attribute on manually added xml string when serializing object?

I am using XmlSerializer to output my object model to XML. Everything works very well but now I need to add several lines of pre-built XML to the object without building classes for each line. After lots of searching, I found that I can convert the xml string to an XmlElement using XmlDocument's LoadXml and DocumentElement calls. I get the XML I want except that the string section has an empty namespace. How can I eliminate the empty namespace attribute? Is there a better way to add an xml string to the object and have it be serialized properly?
Note: I am only creating output so I don't need to deserialize the generated XML. I am fairly new to the C#, .NET world, and hence, XmlSerialize.
Here is my code:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public XmlElement Extension { get; set; }
public Book()
{
}
public void AddExtension()
{
string xmlString = "<AdditionalInfo>" +
"<SpecialHandling>Some Value</SpecialHandling>" +
"</AdditionalInfo>";
this.Extension = GetElement(xmlString);
}
public static XmlElement GetElement(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
}
static void Main(string[] args)
{
TestSerialization p = new TestSerialization();
Book bookOne = new Book();
bookOne.Title = "How to Fix Code";
bookOne.Author = "Dee Bugger";
bookOne.AddExtension();
System.Xml.Serialization.XmlSerializer serializer = new XmlSerializer(typeof(Book), "http://www.somenamespace.com");
using (var writer = new StreamWriter("C:\\BookReport.xml"))
using (var xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings { Indent = true }))
{
serializer.Serialize(xmlWriter, bookOne);
}
}
Here is my output:
<?xml version="1.0" encoding="utf-8"?>
<Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.somenamespace.com">
<Title>How to Fix Code</Title>
<Author>Dee Bugger</Author>
<Extension>
<AdditionalInfo xmlns="">
<SpecialHandling>Some Value</SpecialHandling>
</AdditionalInfo>
</Extension>
</Book>
It is the xmlns="" on AdditionalInfo that I want to eliminate. I believe this coming out because there is no association between the XmlDocument I created and the root serialized object, so the XmlDocument creates its own namespace. How can I tell the XmlDocument (and really, the generated XmlElement) that it belongs to the same namespace as the serialized object?
This is added because the parent elements have a namespace and your AdditionalInfo element does not. The xmlns="" attribute changes the default namespace for that element and its children.
If you want to get rid of it, then presumably you want the AdditionalInfo element to have the same namespace as its parent. In which case, you need to change your XML to this:
string xmlString = #"<AdditionalInfo xmlns=\"http://www.somenamespace.com\">" +
"<SpecialHandling>Some Value</SpecialHandling>" +
"</AdditionalInfo>";

How to deserialise a named XML node and grab all of its children C#

I've the following XML which I can deserialise in C#:
<?xml version="1.0" encoding="iso-8859-1" ?>
<ProductList>
<Product>
<MyDesc><![CDATA[DOCTOR WHO - PLANET OF THE DEA]]></MyDesc>
<ActualStock><![CDATA[5]]></ActualStock>
</Product>
</ProductList>
However, if I add a new node eg MetaData, I do not know how to grab that node and all of its children as text:
<?xml version="1.0" encoding="iso-8859-1" ?>
<ProductList>
<Product>
<MyDesc><![CDATA[DOCTOR WHO - PLANET OF THE DEA]]></MyDesc>
<ActualStock><![CDATA[5]]></ActualStock>
**<MetaData>
<Desc>MD-Description - This is my product desc</Desc>
<Image>MD - The main image</Image>
</MetaData>**
</Product>
</ProductList>
ie I want to grab and store everything within MetaData as one string. The trouble is the children of MetaData are unknown.
Is this possible?
I thought the deserialiser would use this to match the MetaData node:
[XmlElement("MetaData")]
public List<MetaData> MetaDataList
{
get
{
return metaDataList;
}
set
{
metaDataList = value;
}
}
and then figure out MetaData's children using:
private List<MetaData> metaDataList = new List<MetaData>();
public class MetaData
{
[XmlAnyElement]
public List<string> Children
{
get;
set;
}
}
As you can see I'm completely stuck...
Pointers would be appreciated.
Thanks.
Sai.

XML Serializing Element with prefix

<?xml version="1.0" encoding="UTF-8"?>
<rs:model-request xsi:schemaLocation="http://www.ca.com/spectrum/restful/schema/request ../../../xsd/Request.xsd " xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rs="http://www.ca.com/spectrum/restful/schema/request" throttlesize="100">
<rs:target-models>
I am having trouble understanding the C# XmlSerializer. I have successfully been able to serialize elements that do not have a prefix such as rs:* above. I also have no been able to find how to add the xsi:, xmlns:xsi and xmlns:rs (namespaces?).
Would someone be able to create a simple class to show how to generate the above XML?
Fields, properties, and objects can have a namespace associated with them for serialization purposes. You specify the namespaces using attributes such as [XmlRoot(...)], [XmlElement(...)], and [XmlAttribute(...)]:
[XmlRoot(ElementName = "MyRoot", Namespace = MyElement.ElementNamespace)]
public class MyElement
{
public const string ElementNamespace = "http://www.mynamespace.com";
public const string SchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
[XmlAttribute("schemaLocation", Namespace = SchemaInstanceNamespace)]
public string SchemaLocation = "http://www.mynamespace.com/schema.xsd";
public string Content { get; set; }
}
Then you associate the desired namespace prefixes during serialization by using the XmlSerializerNamespaces object:
var obj = new MyElement() { Content = "testing" };
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("xsi", MyElement.SchemaInstanceNamespace);
namespaces.Add("myns", MyElement.ElementNamespace);
var serializer = new XmlSerializer(typeof(MyElement));
using (var writer = File.CreateText("serialized.xml"))
{
serializer.Serialize(writer, obj, namespaces);
}
The final output file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<myns:MyRoot xmlns:myns="http://www.mynamespace.com" xsi:schemaLocation="http://www.mynamespace.com/schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<myns:Content>testing</myns:Content>
</myns:MyRoot>

Change the format of the xml

I am creating a webservice in .net :
WebService :
public class ValidateWebService : System.Web.Services.WebService
{
[WebMethod]
public List<string> ListAllArtists1()
{
List<string> all1 = new List<string>();
all1.Add("Puneet");
all1.Add("03/07/1988");
all1.Add("Delhi");
return all1;
}
}
}
and when I browse this webservice and invoke the method, ths will return me a list in xml format like this :
<?xml version="1.0" encoding="utf-8" ?>
- <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<string>Puneet</string>
<string>03/07/1988</string>
<string>Delhi</string>
</ArrayOfString>
My problem is the format of this xml. I want the xml format like this:
<Name>1</Name>
<DOB>03/07/1988</DOB>
<State>Delhi</State>
So that I can easily read the xml by the name of its nodes.
Please help.
If you want xml in such format, then return object instead of collection of strings:
public Artist GetArtist()
{
Artist artist = new Artist();
artist.Name = "Puneet";
artist.DOB = "03/07/1988"; // consider DateTime type
artist.State = "Delhi";
return artist;
}
This will return serialized Artist instance:
<Artist>
<Name>Puneet</Name>
<DOB>03/07/1988</DOB>
<State>Delhi</State>
</Artist>

Using the .NET serializer to serialize XML to a .NET class

I have an XML file:
<?xml version="1.0" encoding="UTF-8"?>
<MyProducts>
<Product Name="P1" />
<Product Name="P2" />
</MyProducts>
And a C# object:
Public Class Product
{
[XmlAttribute]
Public Name {get; set;}
}
Using the .NET Serializer class now can I Deserialize the XML file into an IList without creating a MyProducts object?
So I want to somehow select only the Product elements before I serialize
There's a quick-and-dirty way to accomplish what you want - simply replace "MyProducts" with something the BCL classes are happy with - ArrayOfProduct:
string xml = #"<?xml version='1.0' encoding='UTF-8;'?>
<MyProducts>
<Product Name='P1' />
<Product Name='P2' />
</MyProducts>";
//secret sauce:
xml = xml.Replace("MyProducts>", "ArrayOfProduct>");
IList myProducts;
using (StringReader sr = new StringReader(xml))
{
XmlSerializer xs = new XmlSerializer(typeof(List<Product>));
myProducts = xs.Deserialize(sr) as IList;
}
foreach (Product myProduct in myProducts)
{
Console.Write(myProduct.Name);
}
Of course, the right way would be to transform the XML document to replace the MyProducts nodes appropriately - instead of using a string replace - but this illustrates the point.
If you don't want to create a collection class for your products, you can mix some LINQ to XML with the XmlSerializer:
public static IEnumerable<T> DeserializeMany<T>(
string fileName, string descendentNodeName = null) {
descendentNodeName = descendentNodeName ?? typeof(T).Name;
var serializer = new XmlSerializer(typeof(T));
return
from item in XDocument.Load(fileName).Descendants(descendentNodeName)
select (T)serializer.Deserialize(item.CreateReader());
}
Then, to get your list:
var products = XmlSerializerUtils.DeserializeMany<Product>(fileName).ToList();
I'm not sure you're going to have much success using the XML serializer to accomplish what you need. It may be simpler for you to manually parse out the XML and map them explicitly, e.g.
XDocument xml = XDocument.Parse(#"<?xml version=""1.0"" encoding=""UTF-8""?>
<MyProducts>
<Product Name=""P1"" />
<Product Name=""P2"" />
</MyProducts>");
foreach(var product in xml.Descendants(XName.Get("Product")))
{
var p = new Product { Name = product.Attribute(XName.Get("Name")).Value };
// Manipulate your result and add to your collection.
}
...
public class Product
{
public string Name { get; set; }
}
If you're using a file, which you most likely are for your XML, just replace the Parse method on the XDocument w/ Load and the appropriate signature.
I don't think you can instruct the serializer to spit out a IList. The serializer can create a MyProduct collection object and fill it with Products. Which sounds like what you want to do.
You can also use LINQ to query the XML document and create a list of IEnumerable as well.
// load from stream if that is the case
// this just uses a file for demonstration purposes
XDocument doc = XDocument.Load("location_of_source.xml");
// select all Product nodes from the root node and create a new Product object using
// object initialization syntax
var listOfProduct = doc.Descendants("Product")
.Select(p => new Product { Name = p.Attribute("Name").Value});
While it's novel to not create the class, doing so saves you a lot of code... You don't even have to use it except when you deserialize.
//Products declaration
[XmlRoot(ElementName = "MyProducts")]
public class MyProducts : List<Product>
{
}
public class Product
{
[XmlAttribute]
public string Name { get; set; }
}
...
[Test]
public void DeserializeFromString()
{
var xml = #"<?xml version='1.0' encoding='UTF-8;'?>
<MyProducts>
<Product Name='P1' />
<Product Name='P2' />
</MyProducts>";
IList<Product> obj = Serialization<MyProducts>.DeserializeFromString(xml);
Assert.IsNotNull(obj);
Assert.AreEqual(2, obj.Count);
}
...
//Deserialization library
public static T DeserializeFromString(string serialized)
{
var byteArray = Encoding.ASCII.GetBytes(serialized);
var memStream = new MemoryStream(byteArray);
return Deserialize(memStream);
}
public static T Deserialize(Stream stream)
{
var xs = new XmlSerializer(typeof(T));
return (T) xs.Deserialize(stream);
}
The problem is that IList is not serializable so you'd have to implement your custom XmlSerializer - walk through xml nodes etc, you can however deserialize your collection into List using out of the box XmlSerializer.
Dont forget to add default constructor to your Product Class.
XmlSerializer serializer = new XmlSerializer(typeof(List<Product>));
FileStream stream = new FileStream(fileName, FileMode.Open);
var product = serializer.Deserialize(sream) as List<Product>;

Categories