Nuclex.Support/Source/XmlHelper.cs

222 lines
8.4 KiB
C#
Raw Permalink Normal View History

2024-06-13 16:36:21 +00:00
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.IO;
using System.Xml;
#if !USE_XMLDOCUMENT
using System.Xml.Linq;
#endif
using System.Xml.Schema;
namespace Nuclex.Support {
/// <summary>Helper routines for handling XML code</summary>
public static class XmlHelper {
#if !NO_XMLSCHEMA
#region class ValidationEventProcessor
/// <summary>Handles any events occurring when an XML schema is loaded</summary>
private class ValidationEventProcessor {
/// <summary>
/// Callback for notifications being sent by the XmlSchema.Read() method
/// </summary>
/// <param name="sender">Not used</param>
/// <param name="arguments">Contains the notification being sent</param>
public void OnValidationEvent(object sender, ValidationEventArgs arguments) {
// We're only interested in the first error, so if we already got
// an exception on record, skip it!
if(this.OccurredException != null) {
return;
}
// Only errors count as reasons to assume loading has failed
if(arguments.Severity == XmlSeverityType.Error) {
// This code uses the ternary operator because I don't know how to
// stimlate a validation error that has no exception and couldn't achieve
// 100% test coverage otherwise. MSDN does not tell whether a validation
// error without an exception can even occur.
this.OccurredException = (arguments.Exception != null) ?
arguments.Exception :
new XmlSchemaValidationException("Unspecified schema validation error");
}
}
/// <summary>Exception that has occurred during schema loading</summary>
public Exception OccurredException;
}
#endregion // class ValidationEventProcessor
/// <summary>Loads a schema from a file</summary>
/// <param name="schemaPath">Path to the file containing the schema</param>
/// <returns>The loaded schema</returns>
public static XmlSchema LoadSchema(string schemaPath) {
using(FileStream schemaStream = openFileForSharedReading(schemaPath)) {
return LoadSchema(schemaStream);
}
}
/// <summary>Loads a schema from the provided stream</summary>
/// <param name="schemaStream">Stream containing the schema that will be loaded</param>
/// <returns>The loaded schema</returns>
public static XmlSchema LoadSchema(Stream schemaStream) {
return LoadSchema(new StreamReader(schemaStream));
}
/// <summary>Loads a schema from a text reader</summary>
/// <param name="schemaReader">Text reader through which the schema can be read</param>
/// <returns>The loaded schema</returns>
public static XmlSchema LoadSchema(TextReader schemaReader) {
ValidationEventProcessor eventProcessor = new ValidationEventProcessor();
XmlSchema schema = XmlSchema.Read(schemaReader, eventProcessor.OnValidationEvent);
if(eventProcessor.OccurredException != null) {
throw eventProcessor.OccurredException;
}
return schema;
}
/// <summary>Attempts to load a schema from a file</summary>
/// <param name="schemaPath">Path to the file containing the schema</param>
/// <param name="schema">Output parameter that will receive the loaded schema</param>
/// <returns>True if the schema was loaded successfully, otherwise false</returns>
public static bool TryLoadSchema(string schemaPath, out XmlSchema schema) {
FileStream schemaStream;
if(!tryOpenFileForSharedReading(schemaPath, out schemaStream)) {
schema = null;
return false;
}
using(schemaStream) {
return TryLoadSchema(schemaStream, out schema);
}
}
/// <summary>Attempts to load a schema from the provided stream</summary>
/// <param name="schemaStream">Stream containing the schema that will be loaded</param>
/// <param name="schema">Output parameter that will receive the loaded schema</param>
/// <returns>True if the schema was loaded successfully, otherwise false</returns>
public static bool TryLoadSchema(Stream schemaStream, out XmlSchema schema) {
return TryLoadSchema(new StreamReader(schemaStream), out schema);
}
/// <summary>Attempts to load a schema from the provided text reader</summary>
/// <param name="schemaReader">Reader from which the schema can be read</param>
/// <param name="schema">Output parameter that will receive the loaded schema</param>
/// <returns>True if the schema was loaded successfully, otherwise false</returns>
public static bool TryLoadSchema(TextReader schemaReader, out XmlSchema schema) {
try {
ValidationEventProcessor eventProcessor = new ValidationEventProcessor();
schema = XmlSchema.Read(schemaReader, eventProcessor.OnValidationEvent);
if(eventProcessor.OccurredException == null) {
return true;
}
}
catch(Exception) {
// Munch!
}
schema = null;
return false;
}
/// <summary>Loads an XML document from a file</summary>
/// <param name="schema">Schema to use for validating the XML document</param>
/// <param name="documentPath">
/// Path to the file containing the XML document that will be loaded
/// </param>
/// <returns>The loaded XML document</returns>
public static XDocument LoadDocument(XmlSchema schema, string documentPath) {
using(FileStream documentStream = openFileForSharedReading(documentPath)) {
return LoadDocument(schema, documentStream);
}
}
/// <summary>Loads an XML document from a stream</summary>
/// <param name="schema">Schema to use for validating the XML document</param>
/// <param name="documentStream">
/// Stream from which the XML document will be read
/// </param>
/// <returns>The loaded XML document</returns>
public static XDocument LoadDocument(XmlSchema schema, Stream documentStream) {
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(schema);
using(XmlReader reader = XmlReader.Create(documentStream, settings)) {
var document = XDocument.Load(reader, LoadOptions.None);
// Create a schema set because the Validate() method only accepts
// schemas in a schemaset
var schemas = new XmlSchemaSet();
schemas.Add(schema);
// Perform the validation and report the first validation error
// encountered to the caller
var validationEventProcessor = new ValidationEventProcessor();
document.Validate(schemas, validationEventProcessor.OnValidationEvent);
if (validationEventProcessor.OccurredException != null) {
throw validationEventProcessor.OccurredException;
}
return document;
}
}
/// <summary>Opens a file for shared reading</summary>
/// <param name="path">Path to the file that will be opened</param>
/// <returns>The opened file's stream</returns>
private static FileStream openFileForSharedReading(string path) {
return new FileStream(
path, FileMode.Open, FileAccess.Read, FileShare.Read
);
}
/// <summary>Opens a file for shared reading</summary>
/// <param name="path">Path to the file that will be opened</param>
/// <param name="fileStream">
/// Output parameter that receives the opened file's stream
/// </param>
/// <returns>True if the file was opened successfully</returns>
private static bool tryOpenFileForSharedReading(string path, out FileStream fileStream) {
try {
fileStream = new FileStream(
path, FileMode.Open, FileAccess.Read, FileShare.Read
);
return true;
}
catch(Exception) {
// Munch!
}
fileStream = null;
return false;
}
#endif // !NO_XMLSCHEMA
}
} // namespace Nuclex.Support