How to properly xml serialize (with the correct schema) - c#

I have a class that I deserialize with:
public static bool FileDeserializer<T>(string xmlFileName, out T element, out string strError)
{
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StreamReader(xmlFileName))
{ element = (T)xmlSerializer.Deserialize(reader); }
strError = string.Empty;
return true;
}
catch (Exception exc)
{
strError = "XmlFileDeserializer exception: " + exc;
element = default(T);
return false;
}
}
and serialize with
public static bool FileSerializer<T>(T value, string strFilename, out string strError)
{
try
{
var serializer = new XmlSerializer(typeof(T));
using (var xmlWriter = XmlWriter.Create(strFilename))
{ serializer.Serialize(xmlWriter, value); }
strError = string.Empty;
return true;
}
catch (Exception exc)
{
strError = "XmlFileSerializer exception: " + exc;
return false;
}
}
Now everything works fine with serialize/deserialize.
But when I try to read it to modify it:
with visual studio it's on a single line (so very hard to read)
with an other editor (xml explorer) i get the message:
The document does not have a specified schema. Click to apply one.
So I have searched for documentation but didn't understand how to put that in my code.
Thanks
---ADD---
Here is the xml in question sorry for not having added it before
<?xml version="1.0" encoding="utf-8"?><CfgData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><DialogFileIniDir>C:\temp</DialogFileIniDir><HorSplitterPos>204</HorSplitterPos><VerSplitterPos>358</VerSplitterPos><GridStep>10</GridStep><Scale>0</Scale><TraceThickness>3</TraceThickness><ShadowsEnabled>false</ShadowsEnabled><Theme>LIGHT</Theme><LightForeground><A>255</A><R>0</R><G>0</G><B>0</B><ScA>1</ScA><ScR>0</ScR><ScG>0</ScG><ScB>0</ScB></LightForeground><LightBackgound><A>255</A><R>245</R><G>245</G><B>245</B><ScA>1</ScA><ScR>0.913098633</ScR><ScG>0.913098633</ScG><ScB>0.913098633</ScB></LightBackgound><LightTraceOkColor><A>255</A><R>255</R><G>255</G><B>255</B><ScA>1</ScA><ScR>1</ScR><ScG>1</ScG><ScB>1</ScB></LightTraceOkColor><LightTraceCancelColor><A>255</A><R>255</R><G>0</G><B>255</B><ScA>1</ScA><ScR>1</ScR><ScG>0</ScG><ScB>1</ScB></LightTraceCancelColor><LightTraceEndColor><A>255</A><R>0</R><G>0</G><B>255</B><ScA>1</ScA><ScR>0</ScR><ScG>0</ScG><ScB>1</ScB></LightTraceEndColor><LightHeaderUnselectedBG><A>255</A><R>0</R><G>206</G><B>209</B><ScA>1</ScA><ScR>0</ScR><ScG>0.6172066</ScG><ScB>0.637596846</ScB></LightHeaderUnselectedBG><LightHeaderSelectedBG><A>255</A><R>0</R><G>0</G><B>255</B><ScA>1</ScA><ScR>0</ScR><ScG>0</ScG><ScB>1</ScB></LightHeaderSelectedBG><LightHeaderStartEndBG><A>255</A><R>105</R><G>105</G><B>105</B><ScA>1</ScA><ScR>0.141263291</ScR><ScG>0.141263291</ScG><ScB>0.141263291</ScB></LightHeaderStartEndBG><LightHeaderFG><A>255</A><R>255</R><G>255</G><B>255</B><ScA>1</ScA><ScR>1</ScR><ScG>1</ScG><ScB>1</ScB></LightHeaderFG><LightBlockBG><A>255</A><R>220</R><G>220</G><B>220</B><ScA>1</ScA><ScR>0.7156935</ScR><ScG>0.7156935</ScG><ScB>0.7156935</ScB></LightBlockBG><LightBlockFG><A>255</A><R>255</R><G>255</G><B>255</B><ScA>1</ScA><ScR>1</ScR><ScG>1</ScG><ScB>1</ScB></LightBlockFG><DarkForeground><A>255</A><R>255</R><G>255</G><B>255</B><ScA>1</ScA><ScR>1</ScR><ScG>1</ScG><ScB>1</ScB></DarkForeground><DarkBackgound><A>255</A><R>19</R><G>56</G><B>53</B><ScA>1</ScA><ScR>0.00651209056</ScR><ScG>0.0395462364</ScG><ScB>0.0356013142</ScB></DarkBackgound><DarkTraceOkColor><A>255</A><R>255</R><G>255</G><B>255</B><ScA>1</ScA><ScR>1</ScR><ScG>1</ScG><ScB>1</ScB></DarkTraceOkColor><DarkTraceCancelColor><A>255</A><R>255</R><G>0</G><B>255</B><ScA>1</ScA><ScR>1</ScR><ScG>0</ScG><ScB>1</ScB></DarkTraceCancelColor><DarkTraceEndColor><A>255</A><R>0</R><G>0</G><B>255</B><ScA>1</ScA><ScR>0</ScR><ScG>0</ScG><ScB>1</ScB></DarkTraceEndColor><DarkHeaderUnselectedBG><A>255</A><R>220</R><G>220</G><B>220</B><ScA>1</ScA><ScR>0.7156935</ScR><ScG>0.7156935</ScG><ScB>0.7156935</ScB></DarkHeaderUnselectedBG><DarkHeaderSelectedBG><A>255</A><R>0</R><G>0</G><B>255</B><ScA>1</ScA><ScR>0</ScR><ScG>0</ScG><ScB>1</ScB></DarkHeaderSelectedBG><DarkHeaderStartEndBG><A>255</A><R>105</R><G>105</G><B>105</B><ScA>1</ScA><ScR>0.141263291</ScR><ScG>0.141263291</ScG><ScB>0.141263291</ScB></DarkHeaderStartEndBG><DarkHeaderFG><A>255</A><R>255</R><G>255</G><B>255</B><ScA>1</ScA><ScR>1</ScR><ScG>1</ScG><ScB>1</ScB></DarkHeaderFG><DarkBlockBG><A>255</A><R>220</R><G>220</G><B>220</B><ScA>1</ScA><ScR>0.7156935</ScR><ScG>0.7156935</ScG><ScB>0.7156935</ScB></DarkBlockBG><DarkBlockFG><A>255</A><R>255</R><G>255</G><B>255</B><ScA>1</ScA><ScR>1</ScR><ScG>1</ScG><ScB>1</ScB></DarkBlockFG></CfgData>

Related

Export an XML content from a TextBox into an XML file

I am trying to create an application that imports an XML file into a TextBox, with the goal to edit the content.
After editing, the user should be able to save the content of the file, but at the same time to validate it. For example,
<Person id="22">
<Name gg="u">John</Name>
<Surname>Jones</Surname>
<PhoneNo>333333333111</PhoneNo>
<Country>Germany</Country>
</Person>
If the user edits the start tag "Name", but forgets to edits the end tag, it should throw an exception.
I have tried
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(MyTextBox.Text);
xmlDoc.Save(fileName);
and
XmlElement DocRoot = xmlDoc.CreateElement("root");
DocRoot.InnerText = MyTextBox.Text;
xmlDoc.AppendChild(DocRoot);
xmlDoc.Save(fileName);
None worked. I am grateful for any help, thank you!
I have though of this solution and it seems to work :) As per the xsd question, I have a generic XML.
try
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(MyTextBox.Text);
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true
};
XmlWriter writer = XmlWriter.Create(fileName, settings);
xmlDoc.Save(writer);
MessageBox.Show("File overwritten to: " + fileName);
}
catch (Exception ex)
{
MessageBox.Show("The textbox content is wrong. ");
}
Seems that you are trying to check if the XML text is well formed and not really if it is valid against a certain definition.
To check if the XML text is well formed, you can try to parse it and verify if it contains any errors. Here is a function that attempts to do that:
class Program
{
static void Main(string[] args)
{
var result = ValidateXml("<Person id=\"22\"><Name>John<Name></Person>");
if (!result.IsValid)
{
Console.WriteLine($"Line number: {result.Exception.LineNumber}");
Console.WriteLine($"Line position: {result.Exception.LinePosition}");
Console.WriteLine($"Message: {result.Exception.Message}");
}
// OUTPUT:
// Line number: 1
// Line position: 35
// Message: The 'Name' start tag on line 1 position 28 does not match the end tag of 'Person'.Line 1, position 35.
}
static ValidationResult ValidateXml(string xml)
{
using (var xr = XmlReader.Create( new StringReader(xml)))
{
try
{
while (xr.Read())
{
}
return ValidationResult.ValidResult;
}
catch (XmlException exception)
{
return new ValidationResult(exception);
}
}
}
public class ValidationResult
{
public static ValidationResult ValidResult = new ValidationResult();
private ValidationResult()
{
IsValid = true;
}
public ValidationResult(XmlException exception)
{
IsValid = false;
Exception = exception;
}
public bool IsValid { get; }
public XmlException Exception { get;}
}
}

Can't find a path to my xml file

I'm trying to find a my XML file called ClassData.xml, but can't find it. The file itself is hidden in a folder called Data. I don't know how to get to the directory of my project where the file is. I'm using ios simulator.
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
this.lbl_Werkt.Text = "het werkt wtf";
XmlDocument Doc = new XmlDocument();
try
{
Doc.Load("/Data/ClassData.xml");
}
catch (Exception error)
{
Console.WriteLine("The File could not be read:");
Console.WriteLine(error.Message);
}
finally
{
foreach (XmlNode node in Doc.SelectNodes("//Warrior"))
{
string Name = node["Name"].InnerText;
lbl_Name.Text = Name;
}
}
}
I have been really dumb! If someone else has this problem, look at your file (In my case xml) and change the build action from Embedded to Content. If you do this you can just do this:
Doc.Load(#"\Data\ClassData.xml");
For a file in your project, try this:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
this.lbl_Werkt.Text = "het werkt wtf";
try{
XmlDocument Doc = new XmlDocument();
string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), #"Data\ClassData.xml");
if(!file.Exists(path))
throw new IOExpection("File not found");
Doc.Load(path);
foreach (XmlNode node in Doc.SelectNodes("//Warrior"))
{
string Name = node["Name"].InnerText;
lbl_Name.Text = Name;
}
}
catch(IOException ioEx)
{
Console.WriteLine("The File could not be read:");
Console.WriteLine(ioEx.Message);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}

Amazon.S3.IO.S3FileInfo().Exists returns 400 bad request for encrypted files

I am using C# and AWSSDK v3 to upload files into an S3 bucket. The file is encrypted using ServerSideEncryptionCustomerMethod. I can upload the file, but if I check if the file exists using S3FileInfo().Exists, an error is thrown as a (400) Bad Request. However, if I comment out the lines that specify encryption in the upload routine, the S3FileInfo().Exists finds the file without throwing an error. What I am doing wrong? Or is there a different way to check if a file exists when it is encrypted?
Here is my upload routine:
public static string wfUpload(Stream pFileStream, string pBucketName, string pKeyName, string pCryptoKey) {
string retVal = "";
try {
using (var lS3Client = new AmazonS3Client()) {
Aes aesEncryption = Aes.Create();
aesEncryption.KeySize = 256;
aesEncryption.GenerateKey();
string lCryptoKey = Convert.ToBase64String(aesEncryption.Key);
PutObjectRequest request = new PutObjectRequest {
BucketName = pBucketName,
Key = pKeyName,
ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256,
ServerSideEncryptionCustomerProvidedKey = lCryptoKey,
};
request.InputStream = pFileStream;
PutObjectResponse response = lS3Client.PutObject(request);
retVal = lCryptoKey;
}
}
catch (AmazonS3Exception s3Exception) {
Console.WriteLine(s3Exception.Message,
s3Exception.InnerException);
throw (s3Exception);
}
catch (Exception e) {
throw (e);
}
return retVal;
}
And my routine to check if the file exists or not:
public static bool wfFileExists(String pBucketName, String pKeyName) {
bool retVal = false;
try {
using (var lS3Client = new AmazonS3Client()) {
if (new Amazon.S3.IO.S3FileInfo(lS3Client, pBucketName, pKeyName).Exists) {
retVal = true;
}
}
}
catch (AmazonS3Exception s3Exception) {
Console.WriteLine(s3Exception.Message,
s3Exception.InnerException);
throw (s3Exception);
}
catch (Exception e) {
throw (e);
}
return retVal;
}
Well, I think the class/method I was using is one of the high level APIs that doesn't support encryption. I changed my code to do a meta-data query to see if anything comes back. If it can't find the file it throws a "NotFound" ErrorCode in the s3Exception that I check for. Hopefully this helps someone else. If someone else suggests a better approach, I'd love to learn it too.
public static bool wfFileExists(String pBucketName, String pKeyName, String pCryptoKey) {
bool retVal = false;
try {
using (var lS3Client = new AmazonS3Client()) {
GetObjectMetadataRequest request = new GetObjectMetadataRequest {
BucketName = pBucketName,
Key = pKeyName,
ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256,
ServerSideEncryptionCustomerProvidedKey = pCryptoKey,
};
GetObjectMetadataResponse lMetaData = lS3Client.GetObjectMetadata(request);
// If an error is not thrown, we found the metadata.
retVal = true;
}
}
catch (AmazonS3Exception s3Exception) {
Console.WriteLine(s3Exception.Message,
s3Exception.InnerException);
if (s3Exception.ErrorCode != "NotFound") {
throw (s3Exception);
}
}
catch (Exception e) {
throw (e);
}
return retVal;
}

Exception loading the XML

I'm using this code to save and restore the XML values ​​but I'm in trouble . Rescue usually works the problem and when I try to load the XML . I get this exception that in the image.
line 105 : string text = el.Attribute("Text").Value;
void SaveData() {
XDocument xmlDocument = new XDocument(new XElement("Pages"));
List<XElement> xmlPages = new List<XElement>();
foreach(KeyValuePair<string, string> doc in documents)
xmlDocument.Root.Add(
new XElement("Page",
new XAttribute("nodeName", GetNodeName(doc.Key)),
new XAttribute("pageGuid", doc.Key),
new XAttribute("Rtf", doc.Value)));
xmlDocument.Root.Add(
new XElement("TextEdit",
new XAttribute("Text", textBox1.Text)));
xmlDocument.Save(GetPathToFile());
}
void LoadData() {
try {
XDocument xmlDocument = XDocument.Load(GetPathToFile());
rootNode.Nodes.Clear();
documents.Clear();
foreach(XElement el in xmlDocument.Root.Elements()) {
string nodeName = el.Attribute("nodeName").Value;
string pageGuid = el.Attribute("pageGuid").Value;
string rtf = el.Attribute("Rtf").Value;
string text = el.Attribute("Text").Value;
rootNode.Nodes.Add(new DataNode(nodeName, pageGuid));
documents.Add(pageGuid, rtf);
textBox1.Text = text;
}
} catch(Exception ex) {
MessageBox.Show("No data loaded. Check XML file" + ex.ToString());
}
treeList1.RefreshDataSource();
}
The exception is clear: There is not such attribute el.Attribute("Text"), so you can't try to get it's value. Check for attribute existence before getting it's value.
After research could solve the case.
Solution:
void LoadData() {
try {
XDocument xmlDocument = XDocument.Load(GetPathToFile());
rootNode.Nodes.Clear();
documents.Clear();
foreach(XElement el in xmlDocument.Root.Elements()) {
switch(el.Name.LocalName) {
case "Page":
string nodeName = el.Attribute("nodeName").Value;
string pageGuid = el.Attribute("pageGuid").Value;
string rtf = el.Attribute("Rtf").Value;
rootNode.Nodes.Add(new DataNode(nodeName, pageGuid));
documents.Add(pageGuid, rtf);
break;
case "Text":
textEdit1.Text = el.Attribute("text").Value;
break;
}
}
} catch(Exception ex) {
MessageBox.Show("No data loaded. Check XML file");
}
treeList1.RefreshDataSource();
}

error in XML document. Unexpected XML declaration. XML declaration must be the first node in the document

There is an error in XML document (8, 20). Inner 1: Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it.
OK, I understand this error.
How I get it, however, is what perplexes me.
I create the document with Microsoft's Serialize tool. Then, I turn around and attempt to read it back, again, using Microsoft's Deserialize tool.
I am not in control of writing the XML file in the correct format - that I can see.
Here is the single routine I use to read and write.
private string xmlPath = System.Web.Hosting.HostingEnvironment.MapPath(WebConfigurationManager.AppSettings["DATA_XML"]);
private object objLock = new Object();
public string ErrorMessage { get; set; }
public StoredMsgs Operation(string from, string message, FileAccess access) {
StoredMsgs list = null;
lock (objLock) {
ErrorMessage = null;
try {
if (!File.Exists(xmlPath)) {
var root = new XmlRootAttribute(rootName);
var serializer = new XmlSerializer(typeof(StoredMsgs), root);
if (String.IsNullOrEmpty(message)) {
from = "Code Window";
message = "Created File";
}
var item = new StoredMsg() {
From = from,
Date = DateTime.Now.ToString("s"),
Message = message
};
using (var stream = File.Create(xmlPath)) {
list = new StoredMsgs();
list.Add(item);
serializer.Serialize(stream, list);
}
} else {
var root = new XmlRootAttribute("MessageHistory");
var serializer = new XmlSerializer(typeof(StoredMsgs), root);
var item = new StoredMsg() {
From = from,
Date = DateTime.Now.ToString("s"),
Message = message
};
using (var stream = File.Open(xmlPath, FileMode.Open, FileAccess.ReadWrite)) {
list = (StoredMsgs)serializer.Deserialize(stream);
if ((access == FileAccess.ReadWrite) || (access == FileAccess.Write)) {
list.Add(item);
serializer.Serialize(stream, list);
}
}
}
} catch (Exception error) {
var sb = new StringBuilder();
int index = 0;
sb.AppendLine(String.Format("Top Level Error: <b>{0}</b>", error.Message));
var err = error.InnerException;
while (err != null) {
index++;
sb.AppendLine(String.Format("\tInner {0}: {1}", index, err.Message));
err = err.InnerException;
}
ErrorMessage = sb.ToString();
}
}
return list;
}
Is something wrong with my routine? If Microsoft write the file, it seems to me that it should be able to read it back.
It should be generic enough for anyone to use.
Here is my StoredMsg class:
[Serializable()]
[XmlType("StoredMessage")]
public class StoredMessage {
public StoredMessage() {
}
[XmlElement("From")]
public string From { get; set; }
[XmlElement("Date")]
public string Date { get; set; }
[XmlElement("Message")]
public string Message { get; set; }
}
[Serializable()]
[XmlRoot("MessageHistory")]
public class MessageHistory : List<StoredMessage> {
}
The file it generates doesn't look to me like it has any issues.
I saw the solution here:
Error: The XML declaration must be the first node in the document
But, in that case, it seems someone already had an XML document they wanted to read. They just had to fix it.
I have an XML document created my Microsoft, so it should be read back in by Microsoft.
The problem is that you are adding to the file. You deserialize, then re-serialize to the same stream without rewinding and resizing to zero. This gives you multiple root elements:
<?xml version="1.0"?>
<StoredMessage>
</StoredMessage
<?xml version="1.0"?>
<StoredMessage>
</StoredMessage
Multiple root elements, and multiple XML declarations, are invalid according to the XML standard, thus the .NET XML parser throws an exception in this situation by default.
For possible solutions, see XML Error: There are multiple root elements, which suggests you either:
Enclose your list of StoredMessage elements in some synthetic outer element, e.g. StoredMessageList.
This would require you to load the list of messages from the file, add the new message, and then truncate the file and re-serialize the entire list when adding a single item. Thus the performance may be worse than in your current approach, but the XML will be valid.
When deserializing a file containing concatenated root elements, create an XML writer using XmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment and iteratively walk through the concatenated root node(s) and deserialize each one individually as shown, e.g., here. Using ConformanceLevel.Fragment allows the reader to parse streams with multiple root elements (although multiple XML declarations will still cause an error to be thrown).
Later, when adding a new element to the end of the file using XmlSerializer, seek to the end of the file and serialize using an XML writer returned from XmlWriter.Create(TextWriter, XmlWriterSettings)
with XmlWriterSettings.OmitXmlDeclaration = true. This prevents output of multiple XML declarations as explained here.
For option #2, your Operation would look something like the following:
private string xmlPath = System.Web.Hosting.HostingEnvironment.MapPath(WebConfigurationManager.AppSettings["DATA_XML"]);
private object objLock = new Object();
public string ErrorMessage { get; set; }
const string rootName = "MessageHistory";
static readonly XmlSerializer serializer = new XmlSerializer(typeof(StoredMessage), new XmlRootAttribute(rootName));
public MessageHistory Operation(string from, string message, FileAccess access)
{
var list = new MessageHistory();
lock (objLock)
{
ErrorMessage = null;
try
{
using (var file = File.Open(xmlPath, FileMode.OpenOrCreate))
{
list.AddRange(XmlSerializerHelper.ReadObjects<StoredMessage>(file, false, serializer));
if (list.Count == 0 && String.IsNullOrEmpty(message))
{
from = "Code Window";
message = "Created File";
}
var item = new StoredMessage()
{
From = from,
Date = DateTime.Now.ToString("s"),
Message = message
};
if ((access == FileAccess.ReadWrite) || (access == FileAccess.Write))
{
file.Seek(0, SeekOrigin.End);
var writerSettings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true, // Optional; remove if compact XML is desired.
};
using (var textWriter = new StreamWriter(file))
{
if (list.Count > 0)
textWriter.WriteLine();
using (var xmlWriter = XmlWriter.Create(textWriter, writerSettings))
{
serializer.Serialize(xmlWriter, item);
}
}
}
list.Add(item);
}
}
catch (Exception error)
{
var sb = new StringBuilder();
int index = 0;
sb.AppendLine(String.Format("Top Level Error: <b>{0}</b>", error.Message));
var err = error.InnerException;
while (err != null)
{
index++;
sb.AppendLine(String.Format("\tInner {0}: {1}", index, err.Message));
err = err.InnerException;
}
ErrorMessage = sb.ToString();
}
}
return list;
}
Using the following extension method adapted from Read nodes of a xml file in C#:
public partial class XmlSerializerHelper
{
public static List<T> ReadObjects<T>(Stream stream, bool closeInput = true, XmlSerializer serializer = null)
{
var list = new List<T>();
serializer = serializer ?? new XmlSerializer(typeof(T));
var settings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment,
CloseInput = closeInput,
};
using (var xmlTextReader = XmlReader.Create(stream, settings))
{
while (xmlTextReader.Read())
{ // Skip whitespace
if (xmlTextReader.NodeType == XmlNodeType.Element)
{
using (var subReader = xmlTextReader.ReadSubtree())
{
var logEvent = (T)serializer.Deserialize(subReader);
list.Add(logEvent);
}
}
}
}
return list;
}
}
Note that if you are going to create an XmlSerializer using a custom XmlRootAttribute, you must cache the serializer to avoid a memory leak.
Sample fiddle.

Categories

Resources