I am writing an application that parses an Xml file. I have the schema (.xsd) file which I use to validate the Xml before trying to deserialize it:
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(null, "./xml/schemas/myschema.xsd");
settings.ValidationType = ValidationType.Schema;
XmlReader reader = XmlReader.Create(xmlFile, settings);
XmlDocument document = new XmlDocument();
document.Load(reader);
ValidationEventHandler eventHandler = new ValidationEventHandler(settings_ValidationEventHandler);
document.Validate(eventHandler);
Note that the parameter *./xml/schemas/myschema.xsd" is the path to the .xsd relative to program execution.
I don't want to use filenames/paths, instead I would rather compile the .xsd file as an embedded resource in my project (I have already added the .xsd file and set the Build Action to Embedded Resource).
My question is.... how do I add the Embedded Resource schema to the XmlReaderSettings schema list? There are 4 overloaded methods for settings.Schemas.Add but none of them take an embedded resource as an argument. They all take the path to the schema file.
I have used embedded resources in the past for dynamically setting label images so I am somewhat familiar with using embedded resources. Looking at my other code it looks like what I eventually end up with is a Stream that contains the content:
System.Reflection.Assembly myAssembly = System.Reflection.Assembly.GetExecutingAssembly();
Stream myStream = myAssembly.GetManifestResourceStream(resourceName);
I am assuming that the embedded .xsd will also be read in as a stream so this narrows down my question a bit. How do I add the schema to XmlReaderSettings when I have a reference to the stream containing the schema and not the filename?
You can use the Add() overload that takes an XmlReader as its second parameter:
Assembly myAssembly = Assembly.GetExecutingAssembly();
using (Stream schemaStream = myAssembly.GetManifestResourceStream(resourceName)) {
using (XmlReader schemaReader = XmlReader.Create(schemaStream)) {
settings.Schemas.Add(null, schemaReader);
}
}
Or you can load the schema first and then add it:
Assembly myAssembly = Assembly.GetExecutingAssembly();
using (Stream schemaStream = myAssembly.GetManifestResourceStream(resourceName)) {
XmlSchema schema = XmlSchema.Read(schemaStream, null);
settings.Schemas.Add(schema);
}
Related
I am trying to validate a xml file againg a xsd, but for some reason the following Errorr occures.
Strange is that different programms, which do the same does not show an error.
How can i change the settings for the xsd to not check for missing attributes and just ignore them?
Code:
static void ValidateXml(string xmlFile, string xsdFile)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(null, xsdFile);
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += ValidationEventHandler;
using (XmlReader reader = XmlReader.Create(xmlFile, settings)) // failing here
{
while(reader.Read())
{
// Reading and doing nothing with the xml file.
}
}
}
Error Message:
System.Xml.Schema.XmlSchemaValidationException: "The 'http://www.w3.org/XML/1998/namespace:lang' attribute is not declared."
I tried it using XMlDocument, but that doesnt work for me, cause i am reading of xml files up to 2 gb.
I am trying to write a C# class that validates a xml file using a DTD file located in another folder that is not in a relative location with the DOCTYPE string, so far, my code is like this:
var settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Parse;
settings.ValidationType = ValidationType.DTD;
settings.XmlResolver = new XmlUrlResolver();
settings.ValidationEventHandler += new ValidationEventHandler(IsLoaded);
using (var reader = XmlReader.Create(new StringReader(xmlString), settings))
{
while (reader.Read()) { }
reader.Close();
}
So far this works fine loading the DTD file from the DOCTYPE string included in the xml file, but the DTD file itself must be kept in a folder that is relative to where the program is being excuted. Is there a way to mingle with the XmlResolver class where I can ask it to get a DTD file from another location on my hard drive, like an absoute path being passed in the find the DTD files instead of using the DOCTYPE string?
I am working with Mismo 2.3.1, dtd based schema. I converted the dtd to xsd and then generated c# code to serialize/deserialze object representations of the xml doc.
Given a valid mismo 2.3.1 xml doc, I can deserialize into my generated C# class.
I have code working to use XmlSerializer along with XmlReaderSettings and XmlSchmeas collection, reading in my converted xsd.
If I put xmlns="http://mySchema..." in the root element, and try to validate intentionally invalid xml, works as expected, my validation event gets pinged with accurate description.
If I take out the xmlns attribute, then i get "could not find schema information for element [my root element]"
Any idea on how to validate xml that comes in without the xmlns spec? Any settings to say to the serializer "use this schema when you come across this element"?
Thanks in advance!
static void Main() {
var settings = new XmlReaderSettings();
settings.NameTable = new NameTable();
var nsMgr = new XmlNamespaceManager(settings.NameTable);
nsMgr.AddNamespace("", "http://example.com/2013/ns"); // <-- set default namespace
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add(null, #"C:\XSDSchema.xsd"); // <-- set schema location for the default namespace
var parserCtx = new XmlParserContext(settings.NameTable, nsMgr, XmlSpace.Default);
using (var reader = XmlReader.Create(#"C:\file.xml", settings, parserCtx)) {
var serializer = new XmlSerializer(typeof(Foo));
Foo f = (Foo)serializer.Deserialize(reader);
}
}
In C# am trying to check to see if an XML file is created, if not create the file and then create the xml declaration, a comment and a parent node.
When I try to load it, it gives me this error:
"The process cannot access the file 'C:\FileMoveResults\Applications.xml' because it is being used by another process."
I checked the task manager to ensure it wasn't open and sure enough there were no open applications of it. Any ideas of what's going on?
Here is the code I am using:
//check for the xml file
if (!File.Exists(GlobalVars.strXMLPath))
{
//create the xml file
File.Create(GlobalVars.strXMLPath);
//create the structure
XmlDocument doc = new XmlDocument();
doc.Load(GlobalVars.strXMLPath);
//create the xml declaration
XmlDeclaration xdec = doc.CreateXmlDeclaration("1.0", null, null);
//create the comment
XmlComment xcom = doc.CreateComment("This file contains all the apps, versions, source and destination paths.");
//create the application parent node
XmlNode newApp = doc.CreateElement("applications");
//save
doc.Save(GlobalVars.strXMLPath);
Here is the code I ended up using to fix this issue:
//check for the xml file
if (!File.Exists(GlobalVars.strXMLPath))
{
using (XmlWriter xWriter = XmlWriter.Create(GlobalVars.strXMLPath))
{
xWriter.WriteStartDocument();
xWriter.WriteComment("This file contains all the apps, versions, source and destination paths.");
xWriter.WriteStartElement("application");
xWriter.WriteFullEndElement();
xWriter.WriteEndDocument();
}
File.Create() returns a FileStream that locks the file until it's closed.
You don't need to call File.Create() at all; doc.Save() will create or overwrite the file.
I would suggest something like this:
string filePath = "C:/myFilePath";
XmlDocument doc = new XmlDocument();
if (System.IO.File.Exists(filePath))
{
doc.Load(filePath);
}
else
{
using (XmlWriter xWriter = XmlWriter.Create(filePath))
{
xWriter.WriteStartDocument();
xWriter.WriteStartElement("Element Name");
xWriter.WriteEndElement();
xWriter.WriteEndDocument();
}
//OR
XmlDeclaration xdec = doc.CreateXmlDeclaration("1.0", null, null);
XmlComment xcom = doc.CreateComment("This file contains all the apps, versions, source and destination paths.");
XmlNode newApp = doc.CreateElement("applications");
XmlNode newApp = doc.CreateElement("applications1");
XmlNode newApp = doc.CreateElement("applications2");
doc.Save(filePath); //save a copy
}
The reason your code is currently having problems is because of: File.Create creates the file and opens the stream to the file, and then you never make use of it (never close it) on this line:
//create the xml file
File.Create(GlobalVars.strXMLPath);
if you did something like
//create the xml file
using(Stream fStream = File.Create(GlobalVars.strXMLPath)) { }
Then you would not get that in use exception.
As a side note XmlDocument.Load will not create a file, only work with an already create one
You could create a stream, setting the FileMode to FileMode.Create and then use the stream to save the Xml to the path specified.
using (System.IO.Stream stream = new System.IO.FileStream(GlobalVars.strXMLPath, FileMode.Create))
{
XmlDocument doc = new XmlDocument();
...
doc.Save(stream);
}
I have an xml file that refers to a local dtd file. But the problem is that my files are being compressed into a single file (I am using Unity3D and it puts all my textfiles into one binary). This question is not Unity3D specific, it is useful for anyone that tries to load a DTD schema from a string.
I have thought of a workaround to load the xml and load the dtd separately and then add the dtd file to the XmlSchemas of my document. Like so:
private void ReadConfig(string filePath)
{
// load the xml file
TextAsset text = (TextAsset)Resources.Load(filePath);
StringReader sr = new StringReader(text.text);
sr.Read(); // skip BOM, Unity3D catch!
// load the dtd file
TextAsset dtdAsset = (TextAsset)Resources.Load("Configs/condigDtd");
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(...); // my dtd should be added into this schemaset somehow, but it's only a string and not a filepath.
XmlReaderSettings settings = new XmlReaderSettings() { ValidationType = ValidationType.DTD, ProhibitDtd = false, Schemas = schemaSet};
XmlReader r = XmlReader.Create(sr, settings);
XmlDocument doc = new XmlDocument();
doc.Load(r);
}
The xml starts like this, but the dtd cannot be found. Not strange, because the xml file was loaded as a string, not from a file.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Scene SYSTEM "configDtd.dtd">
XmlSchema has a Read method that takes in a Stream and a ValidationEventHandler. If the DTD is a string, you could convert it to a stream
System.Text.Encoding encode = System.Tet.Encoding.UTF8;
MemoryStream ms = new MemoryStream(encode.GetBytes(myDTD));
create the XmlSchema
XmlSchema mySchema = XmlSchema.Read(ms, DTDValidation);
add this schema to the XmlDocument containing the xml you are validating
myXMLDocument.Schemas.Add(mySchema);
myXMLDocument.Schemas.Compile();
myXMLDocument.Validate(DTDValidation);
The DTDValidation() handler would contain code handling what to do if the xml is invalid.