Using c# and .net 3.5 I'm trying to validate an xml document against a schema that has includes.
The schemas and there includes are as below
Schema1.xsd -> include another.xsd
another.xsd -> include base.xsd
When i try to add the Schema1.xsd to the XmlDocument i get the following error.
Type 'YesNoType' is not declared or is not a simple type.
I believe i'm getting this error because the base.xsd file is not being included when i load the Schema1.xsd schema.
I'm trying to use the XmlSchemaSet class and I'm setting the XmlResolver uri to the location of the schemas.
NOTE : All schemas live under the same directory E:\Dev\Main\XmlSchemas
Here is the code
string schemaPath = "E:\\Dev\\Main\\XmlSchemas";
XmlDocument xmlDocSchema = new XmlDocument();
XmlSchemaSet s = new XmlSchemaSet();
XmlUrlResolver resolver = new XmlUrlResolver();
Uri baseUri = new Uri(schemaPath);
resolver.ResolveUri(null, schemaPath);
s.XmlResolver = resolver;
s.Add(null, XmlReader.Create(new System.IO.StreamReader(schemaPath + "\\Schema1.xsd"), new XmlReaderSettings { ValidationType = ValidationType.Schema, XmlResolver = resolver }, new Uri(schemaPath).ToString()));
xmlDocSchema.Schemas.Add(s);
ValidationEventHandler valEventHandler = new ValidationEventHandler
(ValidateNinoDobEvent);
try
{
xmlDocSchema.LoadXml(xml);
xmlDocSchema.Validate(valEventHandler);
}
catch (XmlSchemaValidationException xmlValidationError)
{
// need to interogate the Validation Exception, for possible further
// processing.
string message = xmlValidationError.Message;
return false;
}
Can anyone point me in the right direction regarding validating an xmldocument against a schema with nested includes.
I also have a nested schema case and I don't find any error in validating.My code looks like follwoing.
private string strLogger = null;
public bool ValidateXml(string path2XMLFile, string path2XSDFile)
{
bool isValidFile = false;
try
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add(null, path2XSDFile);
settings.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler);
XmlReader reader = XmlReader.Create(path2XMLFile, settings);
while (reader.Read()) ;
if (String.IsNullOrEmpty(strLogger))
{
isValidFile = true;
}
}
catch (Exception ex)
{
LoggingHandler.Log(ex);
}
return isValidFile;
}
private void settings_ValidationEventHandler(object sender, ValidationEventArgs e)
{
strLogger += System.Environment.NewLine + "Validation Error Message = [" + e.Message + "], " + "Validation Error Severity = [" + e.Severity + "], " + System.Environment.NewLine;
}
I think that what you need to do is to merge the schemas:
http://asp.dotnetheaven.com/howto/doc/Xml/MultipleSchemas.aspx
If they're nested, that means that you'll need to start at the bottom of the hierarchy and load them in that order. I'm not 100% sure because the samples I was able to find don't have, strictly speaking, nested structures, but rather complementary structures. Good luck.
Related
I have 2 XML files.
I want to compare them using XML Reader, for this I have already built a XML Reader method and provided the attributes for the Reader Settings.
Now the reader should run through, compare the two into a hash. If the hash is the same, then try catch to delete the old or the new one.
My problem is:
The reader needs an integer to compare the hashes (this is what I see)
However, I can only convert the XML path into a string and then pass it.
How do I get these files into the reader ?
private string xmlOld = #"c:\temp\List-FULL(xsd).xml".ToString();
private string xmlNew = #"c:\temp\List-FULL(xsd)2.xml".ToString();
Here is the Method:
public bool XmlListReader()
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreWhitespace = true;
try
{
int xmlHashNew = settings.GetHashCode(); // Part to Load 2 XML files, with Readersettings.
int xmlHashOld = settings.GetHashCode();
File.Delete(xmlHashAlt.Equals(xmlHashNeu) == false ? xmlOld : xmlNew);
return true;
}
catch (Exception e)
{
Console.WriteLine(e);
return false;
}
}
Thanks in advance
I am trying append a serialized object to an existing xml file beneath the root element, which I thought would be simple but is proving to be a little challenging.
The problem is in the AddShortcut method but I added some more code for completeness.
I believe what I need to do is:
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument.
The trouble is in steps 2 and 3. I have tried different variations but I think using XPathNavigator somehow to find the "root" node to append under is a step in the right direction.
I have also looked at almost every question on Stack Overflow on the subject.
Any suggestions welcome. Here is my code
class XmlEngine
{
public string FullPath { get; set; } // the full path to the xmlDocument
private readonly XmlDocument xDoc;
public XmlEngine(string fullPath, string startElement, string[] rElements)
{
FullPath = fullPath;
xDoc = new XmlDocument();
CreateXmlFile(FullPath, startElement, rElements);
}
public void CreateXmlFile(string path, string startElement, string[] rElements)
{
try
{
if (!File.Exists(path))
{
// create a txt writer
XmlTextWriter wtr = new XmlTextWriter(path, System.Text.Encoding.UTF8);
// make sure the file is well formatted
wtr.Formatting = Formatting.Indented;
wtr.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
wtr.WriteStartElement(startElement);
wtr.Close();
// write the top level root elements
writeRootElements(path, rElements);
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
Console.WriteLine("Could not create file: " + path);
}
}
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
rootNode = xDoc.AppendChild(xDoc.CreateElement("Shortcuts"));
var serializer = new XmlSerializer(s.GetType());
using (var writer = new StreamWriter(FullPath, true))
{
XmlWriterSettings ws = new XmlWriterSettings();
ws.OmitXmlDeclaration = true;
serializer.Serialize(writer, s);
}
xDoc.Save(FullPath);
}
}
This code sample worked for me:
xml:
<?xml version="1.0" encoding="UTF-8"?>
<Launchpad>
<Shortcuts>
<Shortcut Id="1">
<Type>Folder</Type>
<FullPath>C:\SomePath</FullPath>
<Name>SomeFolderName</Name>
</Shortcut>
</Shortcuts>
</Launchpad>
Method:
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
var rootNode = xDoc.GetElementsByTagName("Shortcuts")[0];
var nav = rootNode.CreateNavigator();
var emptyNamepsaces = new XmlSerializerNamespaces(new[] {
XmlQualifiedName.Empty
});
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(s.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, s, emptyNamepsaces);
writer.Close();
}
xDoc.Save(FullPath);
}
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument
So:
public void AddShortcut(Shortcut s)
{
// 1. load existing xml
xDoc.Load(FullPath);
// 2. create an XML node from object
XmlElement node = SerializeToXmlElement(s);
// 3. append that node to Shortcuts node under XML root
var shortcutsNode = xDoc.CreateElement("Shortcuts")
shortcutsNode.AppendChild(node);
xDoc.DocumentElement.AppendChild(shortcutsNode);
// 4. save changes
xDoc.Save(FullPath);
}
public static XmlElement SerializeToXmlElement(object o)
{
XmlDocument doc = new XmlDocument();
using(XmlWriter writer = doc.CreateNavigator().AppendChild())
{
new XmlSerializer(o.GetType()).Serialize(writer, o);
}
return doc.DocumentElement;
}
This post
Here, i created a class, and what i am trying to accomplish is to write the contents from the list into an xml file.
1) At first run, it creates the file and trows an error here: Token EndElement in state EndRootElement would result in an invalid XML document
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//second run, error Occurr here
//xml writer object, CellPhoneProduct
XmlWriter xmlOut = XmlWriter.Create(path, localSettings);
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
//first Run error is thrown in here.
xmlOut.WriteEndElement();
xmlOut.Close();
}
2) When i rerun on the second time, the error is in same method.
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//xml writer object, CellPhoneProduct
XmlWriter xmlOut = XmlWriter.Create(path, localSettings);
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
xmlOut.WriteEndElement();
xmlOut.Close();
}
The whole class i here:
class ProductCellPhoneDB
{
private const string path = #"..\..\CellPhoneProducts.xml";
public static List<ProducCellPhone> GetCellPhoneProducts()
{
List<ProducCellPhone> localCellPhoneProducts =
new List<ProducCellPhone>();
XmlReaderSettings localSettings = new XmlReaderSettings();
localSettings.IgnoreWhitespace = true;
localSettings.IgnoreComments = true;
XmlReader xmlIn = (XmlReader.Create(path,localSettings));
if (xmlIn.ReadToDescendant("Cell"))
{
do
{
ProducCellPhone localProduct = null;
xmlIn.ReadStartElement("Cell");
localCellPhoneProducts.Add(localProduct);
}
while (xmlIn.ReadToNextSibling("Cell"));
}
xmlIn.Close();
return localCellPhoneProducts;
}
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//Error Occurr here
//xml writer object, CellPhoneProduct, error is being used by other process?
XmlWriter xmlOut = (XmlWriter.Create(path, localSettings));
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
//ERROR Token EndElement in state EndRootElement would result in an invalid XML document
xmlOut.WriteEndElement();
xmlOut.Close();
}
private static void ReadCellphoneProductBase(XmlReader xmlIn, ProducCellPhone localProduct)
{
localProduct.Iemi = xmlIn.ReadElementContentAsString();
localProduct.Model = xmlIn.ReadContentAsString();
localProduct.Price = xmlIn.ReadContentAsDecimal();
}
private static void WriteCellPhoneProductBase(ProducCellPhone localProduct,
XmlWriter xmlout)
{
xmlout.WriteElementString("IEMI", localProduct.Iemi);
xmlout.WriteElementString("Model", localProduct.Model);
xmlout.WriteElementString("Price", Convert.ToString(localProduct.Price));
xmlout.WriteEndElement();
}
}
Any suggestions would be helpful. Thanks community. !
The first error
you get is likely because the WriteStartElement and WriteEndElement calls are not matched. You do xmlOut.WriteStartElement("Cell"); once, but do xmlout.WriteEndElement(); several times, once for each ProducCellPhone in LocalProducts, plus another time after the foreach.
To solve this (if I guessed your XML document structure right), you should change your WriteCellPhoneProductBase method to:
private static void WriteCellPhoneProductBase(ProducCellPhone localProduct,
XmlWriter xmlout)
{
xmlOut.WriteStartElement("Cell");
xmlout.WriteElementString("IEMI", localProduct.Iemi);
xmlout.WriteElementString("Model", localProduct.Model);
xmlout.WriteElementString("Price", Convert.ToString(localProduct.Price));
xmlout.WriteEndElement();
}
And remove the WriteStartElement and WriteEndElement lines from SaveCellPhoneProducts (see below).
The second error is probably because the XmlWriter used when you got the first error was not disposed and has not closed the file handle. You should always use a using block to ensure IDisposable resources get disposed, also when an exception occurs. You should change your method to:
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
//xml writer settings
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
using (var xmlOut = XmlWriter.Create(path, localSettings))
{
xmlOut.WriteStartDocument();
//write each product on xml file
foreach(ProducCellPhone localProduct in LocalProducts)
WriteCellPhoneProductBase(localProduct, xmlOut);
xmlOut.WriteEndDocument()
}
}
For your GetCellPhoneProducts follow the same using block approach.
I am looking on Internet how keep the carriage return from XML data but I could not find the answer, so I'm here :)
The objective is to write in a file the content of a XML data. So, if the value of the node contains some "\r\n" data, the soft need to write them in file in order to create new line, but it doesn't write, even with space:preserve.
Here is my test class:
XElement xRootNode = new XElement("DOCS");
XElement xData = null;
//XNamespace ns = XNamespace.Xml;
//XAttribute spacePreserve = new XAttribute(ns+"space", "preserve");
//xRootNode.Add(spacePreserve);
xData = new XElement("DOC");
xData.Add(new XAttribute("FILENAME", "c:\\titi\\prout.txt"));
xData.Add(new XAttribute("MODE", "APPEND"));
xData.Add("Hi my name is Baptiste\r\nI'm a lazy boy");
xRootNode.Add(xData);
bool result = Tools.writeToFile(xRootNode.ToString());
And here is my process class:
try
{
XElement xRootNode = XElement.Parse(xmlInputFiles);
String filename = xRootNode.Element(xNodeDoc).Attribute(xAttributeFilename).Value.ToString();
Boolean mode = false;
try
{
mode = xRootNode.Element(xNodeDoc).Attribute(xWriteMode).Value.ToString().ToUpper().Equals(xWriteModeAppend);
}
catch (Exception e)
{
mode = false;
}
String value = xRootNode.Element(xNodeDoc).Value;
StreamWriter destFile = new StreamWriter(filename, mode, System.Text.Encoding.Unicode);
destFile.Write(value);
destFile.Close();
return true;
}
catch (Exception e)
{
return false;
}
Does anybody have an idea?
If you want to preserve cr lf in element or attribute content when saving a XDocument or XElement you can do that by using certain XmlWriterSettings, namely NewLineHandling to Entitize:
string fileName = "XmlOuputTest1.xml";
string attValue = "Line1.\r\nLine2.";
string elementValue = "Line1.\r\nLine2.\r\nLine3.";
XmlWriterSettings xws = new XmlWriterSettings();
xws.NewLineHandling = NewLineHandling.Entitize;
XDocument doc = new XDocument(new XElement("root",
new XAttribute("test", attValue),
elementValue));
using (XmlWriter xw = XmlWriter.Create(fileName, xws))
{
doc.Save(xw);
}
doc = XDocument.Load(fileName);
Console.WriteLine("att value: {0}; element value: {1}.",
attValue == doc.Root.Attribute("test").Value,
elementValue == doc.Root.Value);
In that example the value are preserved in the round trip of saving and loading as the output of the sample is "att value: True; element value: True."
Heres a useful link I found for parsing an Xml string with carraige returns, line feeds in it.
howto-correctly-parse-using-xelementparse-for-strings-that-contain-newline-character-in
It may help those who are parsing an Xml string.
For those who can't be bothered to click it says use an XmlTextReader instead
XmlTextReader xtr = new XmlTextReader(new StringReader(xml));
XElement items = XElement.Load(xtr);
foreach (string desc in items.Elements("Item").Select(i => (string)i.Attribute("Description")))
{
Console.WriteLine("|{0}|", desc);
}
I have xml files that I read in at runtime, is is possible to validate the xml against an xsd file at runtime? using c#
Try this:
public void ValidateXmlDocument(
XmlReader documentToValidate, string schemaPath)
{
XmlSchema schema;
using (var schemaReader = XmlReader.Create(schemaPath))
{
schema = XmlSchema.Read(schemaReader, ValidationEventHandler);
}
var schemas = new XmlSchemaSet();
schemas.Add(schema);
var settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.Schemas = schemas;
settings.ValidationFlags =
XmlSchemaValidationFlags.ProcessIdentityConstraints |
XmlSchemaValidationFlags.ReportValidationWarnings;
settings.ValidationEventHandler += ValidationEventHandler;
using (var validationReader = XmlReader.Create(documentToValidate, settings))
{
while (validationReader.Read())
{
}
}
}
private static void ValidationEventHandler(
object sender, ValidationEventArgs args)
{
if (args.Severity == XmlSeverityType.Error)
{
throw args.Exception;
}
Debug.WriteLine(args.Message);
}
I GOT CODE TOO! I use this in my tests:
public static bool IsValid(XElement element, params string[] schemas)
{
XmlSchemaSet xsd = new XmlSchemaSet();
XmlReader xr = null;
foreach (string s in schemas)
{ // eh, leak 'em.
xr = XmlReader.Create(
new MemoryStream(Encoding.Default.GetBytes(s)));
xsd.Add(null, xr);
}
XDocument doc = new XDocument(element);
var errored = false;
doc.Validate(xsd, (o, e) => errored = true);
if (errored)
return false;
// If this doesn't fail, there's an issue with the XSD.
XNamespace xn = XNamespace.Get(
element.GetDefaultNamespace().NamespaceName);
XElement fail = new XElement(xn + "omgwtflolj/k");
fail.SetAttributeValue("xmlns", xn.NamespaceName);
doc = new XDocument(fail);
var fired = false;
doc.Validate(xsd, (o, e) => fired = true);
return fired;
}
This one takes in the schemas as strings (file resources within the assembly) and adds them to a schema set. I validate and if its not valid I return false.
If the xml isn't found to be invalid, I do a negative check to make sure my schemas aren't screwed up. Its not guaranteed foolproof, but I have used this to find errors in my schemas.
simpler solution..
try
{
XmlReaderSettings Xsettings = new XmlReaderSettings();
Xsettings.Schemas.Add(null, "personDivideSchema.xsd");
Xsettings.ValidationType = ValidationType.Schema;
XmlDocument document = new XmlDocument();
document.Load("person.xml");
XmlReader reader = XmlReader.Create(new StringReader(document.InnerXml), Xsettings);
while (reader.Read());
}
catch (Exception e)
{
Console.WriteLine(e.Message.ToString());
}