Calling
List<PC> _PCList = new List<PC>();
...add Pc to PCList..
WriteXML<List<PC>>(_PCList, "ss.xml");
Function
public static void WriteXML<T>(T o, string filename)
{
string filePath= Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Genweb2\\ADSnopper\\" + filename;
XmlDocument xmlDoc = new XmlDocument();
XPathNavigator nav = xmlDoc.CreateNavigator();
using (XmlWriter writer = nav.AppendChild())
{
XmlSerializer ser = new XmlSerializer(typeof(List<T>), new XmlRootAttribute("TheRootElementName"));
ser.Serialize(writer, o); // error
}
File.WriteAllText(filePath,xmlDoc.InnerXml);
}
inner exception
Unable to cast object of type 'System.Collections.Generic.List1[PC]' to type
'System.Collections.Generic.List1[System.Collections.Generic.List`1[PC]]'.
Please Help
The problem is with the line
XmlSerializer ser = new XmlSerializer(typeof(List<T>), ...
Your T is already List<PC>, and you're trying to create typeof(List<T>), which will translate to typeof(List<List<PC>>). Simply make it typeof(T) instead.
It should be
typeof(T)
instead of
List<T>
XmlSerializer ser = new XmlSerializer(typeof(T), new XmlRootAttribute("TheRootElementName"));
this line in your code causing problem
XmlSerializer ser = new XmlSerializer(typeof(List<T>),
its creating list of list than is not needed
XmlSerializer ser = new XmlSerializer(typeof(T),
either you do above change or do below changes
There is problem with you method you need to change signature to
public static void WriteXML<T>(List<T> o, string filename)
and call method as below
WriteXML<PC>(_PCList, "ss.xml");
By doing above change might resolve your issue.
Related
I have a object obj with 2 properties p1, p2. and a XElement like:
<root><AA><BB>BB</BB></AA></root>
I'd like to make my Xelement as:
<root><AA><BB>BB</BB><CC><p1>val1</p1><p2>val2</p2></CC></AA></root>
I make a new XElement from obj
XElement x = new XElement("CC",new XElement("p1", obj.p1),new XElement("p2", obj.p2));
and insert it in AA element. is ther a better way by serializing my obj and convert it to XElement? (Because My object can change in the future) . Thanks for any help.
Here is my attempt to use XmlSerializer:
XElement xelem = reqRet.RequestDefinition;
xelem.Descendants("AA").ToList().ForEach(reqitem =>
{
using (MemoryStream ms = new MemoryStream())
{
using (TextWriter tw = new StreamWriter(ms))
{
XmlSerializer ser = new XmlSerializer(typeof(obj));
ser.Serialize(tw, ObjVAL);
schElem = new XElement( XElement.Parse(Encoding.ASCII.GetString(ms.ToArray())));
reqitem.Add(schElem);
}
}
reqitem.Add(schElem);
});
Since you're open to using XmlSerializer, use the XmlRoot attribute; try adding the following to your class declaration:
[XmlRoot(Namespace = "www.contoso.com",
ElementName = "CC",
DataType = "string",
IsNullable=true)]
public class MyObj
{
...
See https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlrootattribute%28v=vs.110%29.aspx for more information.
After that, you can use this code:
XElement xelem = XElement.Parse("<root><AA><BB>BB</BB></AA></root>");
MyObj myObj = new MyObj();
XmlSerializer ser = new XmlSerializer(typeof(MyObj));
foreach (XElement reqitem in xelem.Descendants("AA"))
{
using (MemoryStream ms = new MemoryStream())
{
ser.Serialize(ms, myObj);
reqitem.Add(XElement.Parse(Encoding.UTF8.GetString(ms.ToArray())));
}
}
This gives the desired output.
If you want to remove the XMLNS declarations, you can use .Attributes.Remove() after creating the XElement.
I have a list of Xml messages specifically DataContract messages that i record to a file. And i am trying to deserialize them from file one by one. I do not want to read the whole file into memory at once because i expect it to be very big.
I have an implementation of this serialization and that works. I did this by serializing using a FileStream and reading the bytes and using regular expression to determine the end of element. Then taking the element and using DataContractSerializer to get the actual object.
But i was told I should be using higher level code to do this task and it seems like that should be possible. I have the following code that i think should work but it doesn't.
FileStream readStream = File.OpenRead(filename);
DataContractSerializer ds = new DataContractSerializer(typeof(MessageType));
MessageType msg;
while ((msg = (MessageType)ds.ReadObject(readStream)) != null)
{
Console.WriteLine("Test " + msg.Property1);
}
The above code is fed with an input file containing something along the following lines:
<MessageType>....</MessageType>
<MessageType>....</MessageType>
<MessageType>....</MessageType>
It appears that i can read and deserialize the first element correctly but after that it fails saying:
System.Runtime.Serialization.SerializationException was unhandled
Message=There was an error deserializing the object of type MessageType. The data at the root level is invalid. Line 1, position 1.
Source=System.Runtime.Serialization
I have read somewhere that it is due to the way DataContractSerializer works with padded '\0''s to the end - but i couldn't figure out how to fix this problem when reading from a stream without figuring out the end of MessageType tag in some other way. Is there another Serialization class that i should be using? or perhaps a way around this problem?
Thanks!
When you're deserializing the data from the file, WCF uses by default a reader which can only consume proper XML documents. The document which you're reading isn't - it contains multiple root elements, so it's effectively a fragment. You can change the reader the serializer is using by using another overload of ReadObject, as shown in the example below, to one which accepts fragments (by using the XmlReaderSettings object). Or you can have some sort of wrapping element around the <MessageType> elements, and you'd read until the reader were positioned at the end element for the wrapper.
public class StackOverflow_7760551
{
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
public override string ToString()
{
return string.Format("Person[Name={0},Age={1}]", this.Name, this.Age);
}
}
public static void Test()
{
const string fileName = "test.xml";
using (FileStream fs = File.Create(fileName))
{
Person[] people = new Person[]
{
new Person { Name = "John", Age = 33 },
new Person { Name = "Jane", Age = 28 },
new Person { Name = "Jack", Age = 23 }
};
foreach (Person p in people)
{
XmlWriterSettings ws = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
OmitXmlDeclaration = true,
Encoding = new UTF8Encoding(false),
CloseOutput = false,
};
using (XmlWriter w = XmlWriter.Create(fs, ws))
{
DataContractSerializer dcs = new DataContractSerializer(typeof(Person));
dcs.WriteObject(w, p);
}
}
}
Console.WriteLine(File.ReadAllText(fileName));
using (FileStream fs = File.OpenRead(fileName))
{
XmlReaderSettings rs = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment,
};
XmlReader r = XmlReader.Create(fs, rs);
while (!r.EOF)
{
Person p = new DataContractSerializer(typeof(Person)).ReadObject(r) as Person;
Console.WriteLine(p);
}
}
File.Delete(fileName);
}
}
Maybe your file contains BOM
It's common for UTF-8 encoding
XmlSerializer xml = new XmlSerializer(typeof(MessageType));
XmlDocument xdoc = new XmlDocument();
xdoc.Load(stream);
foreach(XmlElement elm in xdoc.GetElementsByTagName("MessageType"))
{
MessageType mt = (MessageType)xml.Deserialize(new StringReader(elm.OuterXml));
}
I'm trying to use the following code to serialize a list of objects into XDocument, but I'm getting an error stating that "Non white space characters cannot be added to content
"
public XDocument GetEngagement(MyApplication application)
{
ProxyClient client = new ProxyClient();
List<Engagement> engs;
List<Engagement> allEngs = new List<Engagement>();
foreach (Applicant app in application.Applicants)
{
engs = new List<Engagement>();
engs = client.GetEngagements("myConnString", app.SSN.ToString());
allEngs.AddRange(engs);
}
DataContractSerializer ser = new DataContractSerializer(allEngs.GetType());
StringBuilder sb = new StringBuilder();
System.Xml.XmlWriterSettings xws = new System.Xml.XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Indent = true;
using (System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(sb, xws))
{
ser.WriteObject(xw, allEngs);
}
return new XDocument(sb.ToString());
}
What am I doing wrong? Is it the XDocument constructor that doesn't take a list of objects? how do solve this?
I would think that last line should be
return XDocument.Parse(sb.ToString());
And it might be an idea to cut out the serializer altogether, it should be easy to directly create an XDoc from the List<> . That gives you full control over the outcome.
Roughly:
var xDoc = new XDocument( new XElement("Engagements",
from eng in allEngs
select new XElement ("Engagement",
new XAttribute("Name", eng.Name),
new XElement("When", eng.When) )
));
The ctor of XDocument expects other objects like XElement and XAttribute. Have a look at the documentation. What you are looking for is XDocument.Parse(...).
The following should work too (not tested):
XDocument doc = new XDocument();
XmlWriter writer = doc.CreateNavigator().AppendChild();
Now you can write directly into the document without using a StringBuilder. Should be much faster.
I have done the job this way.
private void button2_Click(object sender, EventArgs e)
{
List<BrokerInfo> listOfBroker = new List<BrokerInfo>()
{
new BrokerInfo { Section = "TestSec1", Lineitem ="TestLi1" },
new BrokerInfo { Section = "TestSec2", Lineitem = "TestLi2" },
new BrokerInfo { Section = "TestSec3", Lineitem ="TestLi3" }
};
var xDoc = new XDocument(new XElement("Engagements",
new XElement("BrokerData",
from broker in listOfBroker
select new XElement("BrokerInfo",
new XElement("Section", broker.Section),
new XElement("When", broker.Lineitem))
)));
xDoc.Save("D:\\BrokerInfo.xml");
}
public class BrokerInfo
{
public string Section { get; set; }
public string Lineitem { get; set; }
}
I haven't been able to find a question related to my specific problem.
What I am trying to do is take a list of Xml nodes, and directly deserialize them to a List without having to create a class with attributes.
So the xml (myconfig.xml) would look something like this...
<collection>
<item>item1</item>
<item>item2</item>
<item>item3</item>
<item>etc...</item>
</collection>
In the end I would like a list of items as strings.
The code would look like this.
XmlSerializer serializer = new XmlSerializer( typeof( List<string> ) );
using (XmlReader reader = XmlReader.Create( "myconfig.xml" )
{
List<string> itemCollection = (List<string>)serializer.Deserialize( reader );
}
I'm not 100% confident that this is possible, but I'm guessing it should be. Any help would be greatly appreciated.
Are you married to the idea of using a serializer? If not, you can try Linq-to-XML. (.NET 3.5, C# 3 [and higher])
Based on your provided XML file format, this is the simple code.
// add 'using System.Xml.Linq' to your code file
string file = #"C:\Temp\myconfig.xml";
XDocument document = XDocument.Load(file);
List<string> list = (from item in document.Root.Elements("item")
select item.Value)
.ToList();
Ok, interestingly enough I may have found half the answer by serializing an existing List.
The result I got is as follows...
This following code:
List<string> things = new List<string> { "thing1", "thing2" };
XmlSerializer serializer = new XmlSerializer(typeof(List<string>), overrides);
using (TextWriter textWriter = new StreamWriter("things.xml"))
{
serializer.Serialize(textWriter, things);
}
Outputs a result of:
<?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">
<string>thing1</string>
<string>thing2</string>
</ArrayOfString>
I can override the root node by passing an XmlAttributeOverrides instance to the second parameter of the XmlSerializer constructor. It is created like this:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attributes = new XmlAttributes { XmlRoot = new XmlRootAttribute("collection") };
overrides.Add( typeof(List<string>), attributes );
This will change "ArrayOfString" to "collection". I still have not figured out how to control the name of the string element.
To customize List element names using XmlSerializer, you have to wrap the list.
[XmlRoot(Namespace="", ElementName="collection")]
public class ConfigWrapper
{
[XmlElement("item")]
public List<string> Items{ get; set;}
}
Usage:
var itemsList = new List<string>{"item1", "item2", "item3"};
var cfgIn = new ConfigWrapper{ Items = itemsList };
var xs = new XmlSerializer(typeof(ConfigWrapper));
string fileContent = null;
using (var sw = new StringWriter())
{
xs.Serialize(sw, cfgIn);
fileContent = sw.ToString();
Console.WriteLine (fileContent);
}
ConfigWrapper cfgOut = null;
using (var sr = new StringReader(fileContent))
{
cfgOut = xs.Deserialize(sr) as ConfigWrapper;
// cfgOut.Dump(); //view in LinqPad
if(cfgOut != null)
// yields 'item2'
Console.WriteLine (cfgOut.Items[1]);
}
Output:
// fileContent:
<?xml version="1.0" encoding="utf-16"?>
<collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<item>item1</item>
<item>item2</item>
<item>item3</item>
</collection>
If you don't want to wrap the list, the DataContractSerializer will allow you to custom name the elements if you subclass it:
[CollectionDataContract(Name = "collection", ItemName = "item", Namespace = "")]
public class ConfigWrapper : List<string>
{
public ConfigWrapper() : base() { }
public ConfigWrapper(IEnumerable<string> items) : base(items) { }
public ConfigWrapper(int capacity) : base(capacity) { }
}
Usage And Output:
var cfgIn = new ConfigWrapper{ "item1", "item2", "item3" };
var ds = new DataContractSerializer(typeof(ConfigWrapper));
string fileContent = null;
using (var ms = new MemoryStream())
{
ds.WriteObject(ms, cfgIn);
fileContent = Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine (fileContent);
}
// yields: <collection xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><item>item1</item><item>item2</item><item>item3</item></collection>
ConfigWrapper cfgOut = null;
using (var sr = new StringReader(fileContent))
{
using(var xr = XmlReader.Create(sr))
{
cfgOut = ds.ReadObject(xr) as ConfigWrapper;
// cfgOut.Dump(); //view in LinqPad
if(cfgOut != null)
// yields 'item2'
Console.WriteLine (cfgOut[1]);
}
}
Is there any way to get the xml encoding in the toString() Function?
Example:
xml.Save("myfile.xml");
leads to
<?xml version="1.0" encoding="utf-8"?>
<Cooperations>
<Cooperation>
<CooperationId>xxx</CooperationId>
<CooperationName>Allianz Konzern</CooperationName>
<LogicalCustomers>
But
tb_output.Text = xml.toString();
leads to an output like this
<Cooperations>
<Cooperation>
<CooperationId>xxx</CooperationId>
<CooperationName>Allianz Konzern</CooperationName>
<LogicalCustomers>
...
Either explicitly write out the declaration, or use a StringWriter and call Save():
using System;
using System.IO;
using System.Text;
using System.Xml.Linq;
class Test
{
static void Main()
{
string xml = #"<?xml version='1.0' encoding='utf-8'?>
<Cooperations>
<Cooperation />
</Cooperations>";
XDocument doc = XDocument.Parse(xml);
StringBuilder builder = new StringBuilder();
using (TextWriter writer = new StringWriter(builder))
{
doc.Save(writer);
}
Console.WriteLine(builder);
}
}
You could easily add that as an extension method:
public static string ToStringWithDeclaration(this XDocument doc)
{
if (doc == null)
{
throw new ArgumentNullException("doc");
}
StringBuilder builder = new StringBuilder();
using (TextWriter writer = new StringWriter(builder))
{
doc.Save(writer);
}
return builder.ToString();
}
This has the advantage that it won't go bang if there isn't a declaration :)
Then you can use:
string x = doc.ToStringWithDeclaration();
Note that that will use utf-16 as the encoding, because that's the implicit encoding in StringWriter. You can influence that yourself though by creating a subclass of StringWriter, e.g. to always use UTF-8.
The Declaration property will contain the XML declaration. To get the contents plus declaration, you can do the following:
tb_output.Text = xml.Declaration.ToString() + xml.ToString()
use this:
output.Text = String.Concat(xml.Declaration.ToString() , xml.ToString())
I did like this
string distributorInfo = string.Empty;
XDocument distributors = new XDocument();
//below is important else distributors.Declaration.ToString() throws null exception
distributors.Declaration = new XDeclaration("1.0", "utf-8", "yes");
XElement rootElement = new XElement("Distributors");
XElement distributor = null;
XAttribute id = null;
distributor = new XElement("Distributor");
id = new XAttribute("Id", "12345678");
distributor.Add(id);
rootElement.Add(distributor);
distributor = new XElement("Distributor");
id = new XAttribute("Id", "22222222");
distributor.Add(id);
rootElement.Add(distributor);
distributors.Add(rootElement);
distributorInfo = String.Concat(distributors.Declaration.ToString(), distributors.ToString());
Please see below for what I get in distributorInfo
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Distributors>
<Distributor Id="12345678" />
<Distributor Id="22222222" />
<Distributor Id="11111111" />
</Distributors>
Similar to the other +1 answers, but a bit more detail about the declaration, and a slightly more accurate concatenation.
<xml /> declaration should be on its own line in a formatted XML, so I'm making sure we have the newline added.
NOTE: using Environment.Newline so it will produce the platform specific newline
// Parse xml declaration menthod
XDocument document1 =
XDocument.Parse(#"<?xml version=""1.0"" encoding=""iso-8859-1""?><rss version=""2.0""></rss>");
string result1 =
document1.Declaration.ToString() +
Environment.NewLine +
document1.ToString() ;
// Declare xml declaration method
XDocument document2 =
XDocument.Parse(#"<rss version=""2.0""></rss>");
document2.Declaration =
new XDeclaration("1.0", "iso-8859-1", null);
string result2 =
document2.Declaration.ToString() +
Environment.NewLine +
document2.ToString() ;
Both results produce:
<?xml version="1.0" encoding="iso-8859-1"?>
<rss version="2.0"></rss>
A few of these answers solve the poster's request, but seem overly complicated. Here's a simple extension method that avoids the need for a separate writer, handles a missing declaration and supports the standard ToString SaveOptions parameter.
public static string ToXmlString(this XDocument xdoc, SaveOptions options = SaveOptions.None)
{
var newLine = (options & SaveOptions.DisableFormatting) == SaveOptions.DisableFormatting ? "" : Environment.NewLine;
return xdoc.Declaration == null ? xdoc.ToString(options) : xdoc.Declaration + newLine + xdoc.ToString(options);
}
To use the extension, just replace xml.ToString() with xml.ToXmlString()
You can also use an XmlWriter and call the
Writer.WriteDocType()
method.
string uploadCode = "UploadCode";
string LabName = "LabName";
XElement root = new XElement("TestLabs");
foreach (var item in returnList)
{
root.Add(new XElement("TestLab",
new XElement(uploadCode, item.UploadCode),
new XElement(LabName, item.LabName)
)
);
}
XDocument returnXML = new XDocument(new XDeclaration("1.0", "UTF-8","yes"),
root);
string returnVal;
using (var sw = new MemoryStream())
{
using (var strw = new StreamWriter(sw, System.Text.UTF8Encoding.UTF8))
{
returnXML.Save(strw);
returnVal = System.Text.UTF8Encoding.UTF8.GetString(sw.ToArray());
}
}
// ReturnVal has the string with XML data with XML declaration tag
Extension method to get the Xml Declaration included, using string interpolation here and chose to add a new line after xml declaration as this is the standard I guess.
public static class XDocumentExtensions {
public static string ToStringIncludeXmlDeclaration(this XDocument doc){
return $"({((doc.Declaration != null ? doc.Declaration.ToString() +
Environment.NewLine : string.Empty) + doc.ToString())}";
}
}
}
Usage:
tb_output.Text = xml.ToStringIncludeXmlDeclaration();