Dealing with xsd:include XSDs as an embedded resource in Visual Studio - c#

I have a set of XSDs that validate against XMLSPY and against Java code. I need to bring this set of XSDs as an embedded resource in Visual Studio 2012 .net. Unfortunately I am getting an error that a global element has already been declared when trying to resolve them with a custom XmlResolver to deal with the xsd:include. Error is strange because the element is declared only once.
Visual Studio Solution
|----------- Visual Studio Project
|----------- Schemas (Embedded Resource)
|----------- Directory A
|------------ set of XSDs that are referenced by XSDs in Directory B and to a globaltype definition file located in this directory
|----------- Directory B
|------------- set of XSDs that reference each other and those in Directory A, the XSD call from the main is located here
Validating Util Class
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
namespace ABC.XYZ.Utils
{
public static class XmlUtil
{
private static bool isValid;
public static bool ValidateXml(string targetNamespace, string schemaUri, string xml)
{
isValid = true;
var schemaReaderSettings = new XmlReaderSettings() { ValidationType = ValidationType.Schema };
schemaReaderSettings.ValidationEventHandler += MyValidationHandler;
schemaReaderSettings.Schemas.XmlResolver = new XmlResourceResolver();
var schemaReader = XmlReader.Create(GetSchemaStream(schemaUri), schemaReaderSettings);
schemaReaderSettings.Schemas.Add(targetNamespace, schemaReader);
var x = XElement.Parse(xml);
var sr = new System.IO.StringReader(x.ToString());
XmlReader validatingReader = XmlReader.Create(sr, schemaReaderSettings);
while (validatingReader.Read())
{
}
validatingReader.Close();
return isValid;
}
private static void MyValidationHandler(object sender, ValidationEventArgs args)
{
Console.WriteLine("***Validation error");
Console.WriteLine("\tSeverity:{0}", args.Severity);
Console.WriteLine("\tMessage:{0}", args.Message);
isValid = false;
}
private static Stream GetSchemaStream(string relativeFileName)
{
var resourceFileName =
Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.FirstOrDefault(p => p.EndsWith(relativeFileName));
return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFileName);
}
}
}
Custom XmlResolver
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace ABC.XYZ.Utils
{
public class XmlResourceResolver : XmlResolver
{
public const string AssemblyDefaultNamespace = "ABC.XYZ";
public const string SchemasNamespace = "Schemas";
public override Uri ResolveUri(Uri baseUri, string relativeUri)
{
var result = new UriBuilder("res://", AssemblyDefaultNamespace, -1, SchemasNamespace.Replace(".", "/"));
result.Path += "/" + relativeUri.Replace("../", "/").TrimStart('/');
return result.Uri;
}
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
{
if (absoluteUri.Scheme != "res") return null;
Debug.WriteLine("Loading resource based on location {0}", absoluteUri);
var assembly = Assembly.GetExecutingAssembly();
var name = String.Format(CultureInfo.InvariantCulture, "{0}{1}",
absoluteUri.Host,
absoluteUri.GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped).Replace("/", "."));
// try for an exact match based on schemaLocation hint path
var resourceName = (from x in assembly.GetManifestResourceNames()
where name.Equals(x, StringComparison.OrdinalIgnoreCase)
select x).FirstOrDefault();
// if not match based on filename alone
if (resourceName == null)
{
var schemaDocumentName = Path.GetFileName(absoluteUri.AbsolutePath);
Debug.WriteLine("Unable to locate exact match, looking for match based on filename {0}", schemaDocumentName);
resourceName = (from x in assembly.GetManifestResourceNames()
where x.Contains(SchemasNamespace) &&
x.EndsWith("." + schemaDocumentName, StringComparison.OrdinalIgnoreCase)
select x).FirstOrDefault();
}
Debug.WriteLine("Loading resource {0}", resourceName);
var stream = assembly.GetManifestResourceStream(resourceName);
return stream;
}
}
}
Any insights into this problem with be greatly appreciated.

XSD 1.0 encourages but does not require validators to detect multiple inclusions (or imports) of the same schema document and include them only once.
The result is that including the same schema document more than once from multiple other schema documents is the simplest way to create interoperability nightmares with XSD. (Not the only way, just the simplest way.) If you own the schema documents, segregate all inclusions and all schema-location information on imports into a driver file and delete all includes and all schema-location hints on imports from the 'normal' schema documents.

The problem is that when you're doing it with XMLSpy, the XSDs are files in the file system; what's happening then, for each file there's a base URI, and therefore resolvers will use that information to ensure that once an XSD is loaded, the same one is not loaded again, based on URI compare.
Now, the way you're doing it by loading as a stream from an assembly, all that information is gone (your stream doesn't have a base URI). Your resolver will keep loading the same XSD over and over again, from different places, thus creating this clash.
All the XSD processors I know, do not employ any other means to filter multiple inclusions of the same XSD content, but base source URI.
In .NET, the easiest way might be (again, depending on how complex your graph is) to try the solution in this post; the whole idea is to provide a base URI, which should give the info required to avoid multiple inclusions.
Another alternative might be to make sure in your custom resolver that for any given URI you're resolving, you only return once a stream (return null in all other cases). This is guaranteed to work as long as you're not using xsd:redefine composition (in which case the solution is to make a topological sort of the schema file graph and ensure all xsd:redefines are loaded first).
To #CMSperbergMcQueen point, an approach that is guaranteed to work is to refactor ALL the XSDs such that there's only one XSD embedded resource per namespace; each XSD would have all imports removed (technique called "dangling"). Add those XSDs to an XML Schema set as independent XSDs and compile. Unless you run into a .NET bug, the result should be a compiled XmlSchemaSet.

Related

How do I solve: The type or namespace Windows does not exist in namespace Microsoft?

How do I solve this missing dependency? Googling around for this problem, it seems rare. I see similar ones like The type or namespace name 'Windows' does not exist in the namespace 'System' but nowhere do I see someone explain this particular message.
Log files naturally recorded by windows at locations such as C:\Windows\System32\WDI\LogFiles\BootPerfDiagLogger.etl record useful forensic security info, such as every process that ran persistently at boot.
My goal is to parse these files into some intermediary structure like XML or JSON so I can import the data to Python.
I wanted to parse Windows ETL files in Python for forensic / security data science. I thought this would be easy since there's a Python library for it but upon running that library, it doesn't work and is probably no longer maintained.
Luckily I found a Microsoft dev blog on parsing ETL files with the same classes Windows exposes to allow Windows Performance Analyzer to do it.
The example code shown was like:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Windows.EventTracing;
using Microsoft.Windows.EventTracing.Disk;
using Microsoft.Windows.EventTracing.Processes;
class FileOperation
{
public IProcess IssuingProcess;
public string Operation;
public string Path;
public long Size;
public decimal Duration;
}
class Program
{
public static void Main(string[] args)
{
var etlFileName = args[0];
var diskTrace = Path.GetFileNameWithoutExtension(etlFileName) + "-disk.csv";
var fileTrace = Path.GetFileNameWithoutExtension(etlFileName) + "-file.csv";
using (ITraceProcessor trace = TraceProcessor.Create(etlFileName))
{
var pendingDisk = trace.UseDiskIOData();
var pendingFile = trace.UseFileIOData();
trace.Process();
ProcessDisk(pendingDisk.Result, diskTrace);
ProcessFile(pendingFile.Result, fileTrace);
}
}
I won't include the ProcessDisk and ProcessFile classes here because those seem to be geared toward whatever debugging purpose the writer had. Rather, I'm interested in trace. Based on the methods I see called: UseDiskIOData, UseFileIOData, presumably there is a longer list of methods like that I could use to access all available data for each trace.
My immediate goal with this question is just to view what methods exist on the trace object.
So I did some research on how you look at all properties on an object in C#, and there are plenty of answers, that's probably not a problem:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Windows.EventTracing;
using Microsoft.Windows.EventTracing.Disk;
using Microsoft.Windows.EventTracing.Processes;
class Program
{
public static void Main(string[] args)
{
var etlFileName = args[0];
#var diskTrace = Path.GetFileNameWithoutExtension(etlFileName) + "-disk.csv";
#var fileTrace = Path.GetFileNameWithoutExtension(etlFileName) + "-file.csv";
using (ITraceProcessor trace = TraceProcessor.Create(etlFileName))
{
Type myType = trace.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
foreach (PropertyInfo prop in props)
{
object propValue = prop.GetValue(trace, null);
// Do something with propValue
Console.WriteLine(propValue);
}
#var pendingDisk = trace.UseDiskIOData();
#var pendingFile = trace.UseFileIOData();
#trace.Process();
#ProcessDisk(pendingDisk.Result, diskTrace);
#ProcessFile(pendingFile.Result, fileTrace);
}
}
But what I did have a problem with is this:
The type or namespace Windows does not exist in namespace Microsoft
As I said, I looked around for solutions to this and found nothing.

XmlReaderSettings.Schemas.Add() : The URI prefix is not recognized

I upgraded my old application in .Net 4.5. There are some obsolete methods warnings I was getting so thought to resolve them. One of the obsolete methods is XmlValidatingReader.
Looked up on the internet and found that XmlReaderSettings is a potential alternate of XmlValidatingReader.
// ==old code==
Hashtable _SchemasCache = new Hashtable();
XmlReader xmlReader = new XmlTextReader(xmlStream);
XmlValidatingReader validatingReader = new XmlValidatingReader(xmlReader);
validatingReader.Schemas.Add(root.Namespace, schemaLocation); // both parametres are string. No error
_SchemasCache.Add(schemaLocation, validatingReader.Schemas);
// ==new code==
var schemaLocation = "res://somepath/Messages.xsd";
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(root.Namespace, schemaLocation); // this line gives error
_SchemasCache.Add(schemaLocation, settings.Schemas);
old code doesn't give any error but the new code gives an error of The URI prefix is not recognized. I couldn't find the reason for this behavior of settings.Schemas.Add(), as it is working fine with XmlValidatingReader. Can anyone help with this?
Edit: Here value of schemaLocation is "res://somepath/Messages.xsd". Because schemaLocation has no Http: or https:// or not a local resource, that is why the error is occurring. How can I add schemas with these values using XmlReaderSettings
Edit 2: as this XSD is an embedded resource, I found some code online for this scenario. I made below code changes.
Assembly asm = Assembly.Load("AssemblyNameWhereXSDis");
Uri uri = new Uri(#"res://p.a.t.h/Autorisatie/Messages.xsd");
string resourceName1 = asm.GetName().Name + uri.AbsolutePath.Replace("/", ".");
using (Stream schemaStream = myAssembly.GetManifestResourceStream(resourceName1))
{
using (XmlReader schemaReader = XmlReader.Create(schemaStream)) // this line gives error : value(schemaStream) cannot be null
{
settings.Schemas.Add(root.Namespace, schemaReader);
}
}
here, the value of schemaStream is null. And the value of resourceName1 is assemblyname.folder.Message.xsd.
I have made Message.xsd as Embedded Resource from Visual Studio but still not working.
Source of issue
As you figured yourself - URI has to point to a REAL file somewhere - either a URL (HTTP/HTTPS) or a local file ("C:\...").
So, if you prefer using an Embedded Resource instead, you need to use a fully-specified path in the following form:
"Namespace.FolderName.Filename.Extension"
Example
using System;
using System.Linq;
using System.Reflection;
using System.Xml;
// ...
// get full resourceName from current assembly using Linq
var messagesResourceFullName = Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.Where(n => n.EndsWith("Messages.xsd"));
using (var schemaStream = asm.GetManifestResourceStream(messagesResourceFullName))
{
if (schemaStream == null) throw new FileNotFoundException();
using (var schemaReader = XmlReader.Create(schemaStream))
{
settings.Schemas.Add(root.Namespace, schemaReader);
}
}
source
Add this line in your code :
using System.Linq;

Fully Qualified Namespace Metadata Error with Roslyn

I am trying to make a code analyzer which checks for fully qualified using statements. This link has been incredibly helpful, and the basis for my solution (How can I get the fully qualified namespace from a using directive in Roslyn?) but I am running into a problem when I try to access the location of the symbol for the using directive. My code looks like this:
private static void AnalyzeModel(SemanticModelAnalysisContext semanticModelAnalysisContext)
{
var semanticModel = semanticModelAnalysisContext.SemanticModel;
var root = semanticModel.SyntaxTree.GetRoot();
// compare each using statement's name with its fully qualified name
foreach (var usingDirective in root.DescendantNodes().OfType<UsingDirectiveSyntax>())
{
var symbol = semanticModel.GetSymbolInfo(usingDirective.Name).Symbol;
var fullyQualifiedName = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
if (fullyQualifiedName.Contains(GlobalTag))
{
fullyQualifiedName = fullyQualifiedName.Substring(GlobalTag.Length);
}
if (usingDirective.Name.ToString() != fullyQualifiedName)
{
// for each name that is not fully qualified, produce a diagnostic.
var diagnostic = Diagnostic.Create(Rule, symbol.Locations[0], symbol.Name);
semanticModelAnalysisContext.ReportDiagnostic(diagnostic);
}
}
}
The problem is the symbol.Locations[0] only contains items in metadata, not items in source. This leads to the following error:
Assert.IsTrue failed. Test base does not currently handle diagnostics in metadata locations.
My source in my unit tests looks like this:
private const string incorrectSourceCode = #"
namespace System
{
using IO;
using Threading;
}";
Why are there no items in symbol.Locations that are in source? Is there another place I can get this location? I've tried using symbol.ContainingSymbol.Locations[0] or symbol.ContainingNamespace.Locations[0], but those are not referring to the using specific using that I am interested in. I've been pulling out my hair over this for hours, and some clarity would be very greatly appreciated.
Thanks in advance!
Symbol contains MetadateLocation, so if you want to see SourceLocation just retrieve it from the appropriate SyntaxNode:
var diagnostic = Diagnostic.Create(Rule, usingDirective.Name.GetLocation(), symbol.Name)
instead of
var diagnostic = Diagnostic.Create(Rule, symbol.Locations[0], symbol.Name)

Programmatically creating a TFS iteration for a known deep path

Creating a simple iteration path in TFS 2013 is described here. Traversing a whole tree of unknown depth is described here. I need to create an iteration path of which I know the exact path, and which contains sub-directories, as in \{ProjectName}\Iteration\{Year}\{Iteration}
EDIT:
In order to do that safely, I need to first check the existence of the iteration path, which requires me to check the existence of {Year} and {Iteration}. Otherwise an exception is thrown and I'd like to avoid exception-based logic.
I can find only one way of doing that, and it is level by level, using the method CommonStructureService.GetNodesXml(), but then I have to parse XML and I lose the advantage of using the provided API types such as NodeInfo. Is there a better way to check for existence of a deeper child with a known path while retaining the API domain model?
You can create the iterations one by one: Create the node {Year} first, and then create the {Iteration} under {Year}. See following code for details:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
namespace AAAPI
{
class Program
{
static void Main(string[] args)
{
string project = "https://xxx.xxx.xxx.xxx/tfs";
string projectName = "XXX";
string node1 = "Year";
string node2 = "Iter1";
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri(project));
tpc.Authenticate();
Console.WriteLine("Creating node" + node1);
var css = tpc.GetService<ICommonStructureService>();
string rootNodePath = string.Format("\\{0}\\Iteration", projectName);
var pt = css.GetNodeFromPath(rootNodePath);
css.CreateNode(node1, pt.Uri);
Console.WriteLine("Creating" + node1 + "Successfully");
Console.WriteLine("Creating node" + node2);
string parentNodePath = string.Format("\\{0}\\Iteration\\{1}", projectName, node1);
var pt1 = css.GetNodeFromPath(parentNodePath);
css.CreateNode(node2, pt1.Uri);
Console.WriteLine("Creating" + node2 + "Successfully");
Console.ReadLine();
}
}
}
As no valid answers have come I'm going to assume the following answer:
No, there's no way to read any deeper part than the top level while retaining the API typed domain model.
XML is currently the only option.

BundleTransformer.Less inject variables depending on context/request

We would like the use the bundling mechanism of System.Web.Optimization in combination with the Less transformer.
The problem is that the same application/server serves pages for different branded websites. So depending on the 'SiteContext' the same .less files are used but different values should be used by the .less variables. So we want the (re)use the same less files but with different variables depending on the context of the request.
I tried a couple of different theories:
In all 3 cases I setup different bundles depending on the SiteContext.
1 inject an #import directive with the themed variables by using a custom VirtualPathProvider that intercepts the variables.less file.
So I have:
the styling file eg: header.less (imports the variables file)
the variables file: variables.less
a themed variables file: variables-theme.less (injected in variables.less via the VirtualPathProvider)
This is not working because the BundleTransformer cache sees this as the same file and doesn't know about the SiteContext. The cache key is based on the Url of the IAsset and we cannot influence this behavior.
2 Replace the variables.less import by variables-themed.less with an custom transformer that runs before the Less transformer.
Again no luck, same caching issues.
And as a side effect, the extra transformer was not called in debug because the assets are not bundled but called individually by the LessAssetHandler. This could be solved by writing your own AssetHandler that calls all required transformers.
3 create themed Asset names that are resolved by a custom VirtualPathProvider
Eg. Add header-themeX.less to the bundle, this file doesn't exist but you resolve this file to header.less and use method 2 to set the correct variables file import. (replace the import of the variables.less to the themed version).
Once again no luck. I think this could solve the caching issue if it wasn't for the Bundle.Include(string virtualPath) that does a File.Exists(path) internally. It doesn't pass via the CustomVirtualPathProvider.
Am I looking to deep to solve this?
All ideas are welcome, I can imagine that this will become a problem to more and more people as the System.Web.Optimization library gets more popular...
Keep in mind that:
we have a lot of .less/css files
we will have 5 or so themes
we like to keep things working in visual studio (that is why header.less has a ref. to variables.less)
Thanks for any feedback.
Michael!
You use the Microsoft ASP.NET Web Optimization Framework and the Bundle Transformer in multi-tenant environment, so you need to replace some components of the System.Web.Optimization and create own versions of the debugging HTTP-handlers (see «Problem: LESS file imports are added to BundleResponse.Files collection» discussion). As far as I know, Murat Cakir solve all these problems in the SmartStore.NET project.
In the Bundle Transformer there are 2 ways to inject of LESS-variables:
Look a properties GlobalVariables and ModifyVariables of LESS-translator:
using System.Collections.Generic;
using System.Web.Optimization;
using BundleTransformer.Core.Builders;
using BundleTransformer.Core.Orderers;
using BundleTransformer.Core.Transformers;
using BundleTransformer.Core.Translators;
using BundleTransformer.Less.Translators;
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
var nullBuilder = new NullBuilder();
var nullOrderer = new NullOrderer();
var lessTranslator = new LessTranslator
{
GlobalVariables = "my-variable='Hurrah!'",
ModifyVariables = "font-family-base='Comic Sans MS';body-bg=lime;font-size-h1=50px"
};
var cssTransformer = new CssTransformer(new List<ITranslator>{ lessTranslator });
var commonStylesBundle = new Bundle("~/Bundles/BootstrapStyles");
commonStylesBundle.Include(
"~/Content/less/bootstrap-3.1.1/bootstrap.less");
commonStylesBundle.Builder = nullBuilder;
commonStylesBundle.Transforms.Add(cssTransformer);
commonStylesBundle.Orderer = nullOrderer;
bundles.Add(commonStylesBundle);
}
}
Create a custom item transformation:
using System.Text;
using System.Web.Optimization;
public sealed class InjectContentItemTransform : IItemTransform
{
private readonly string _beforeContent;
private readonly string _afterContent;
public InjectContentItemTransform(string beforeContent, string afterContent)
{
_beforeContent = beforeContent ?? string.Empty;
_afterContent = afterContent ?? string.Empty;
}
public string Process(string includedVirtualPath, string input)
{
if (_beforeContent.Length == 0 && _afterContent.Length == 0)
{
return input;
}
var contentBuilder = new StringBuilder();
if (_beforeContent.Length > 0)
{
contentBuilder.AppendLine(_beforeContent);
}
contentBuilder.AppendLine(input);
if (_afterContent.Length > 0)
{
contentBuilder.AppendLine(_afterContent);
}
return contentBuilder.ToString();
}
}
And register this transformation as follows:
using System.Web.Optimization;
using BundleTransformer.Core.Orderers;
using BundleTransformer.Core.Bundles;
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
var nullOrderer = new NullOrderer();
const string beforeLessCodeToInject = #"#my-variable: 'Hurrah!';";
const string afterLessCodeToInject = #"#font-family-base: 'Comic Sans MS';
#body-bg: lime;
#font-size-h1: 50px;";
var commonStylesBundle = new CustomStyleBundle("~/Bundles/BootstrapStyles");
commonStylesBundle.Include(
"~/Content/less/bootstrap-3.1.1/bootstrap.less",
new InjectContentItemTransform(beforeLessCodeToInject, afterLessCodeToInject));
commonStylesBundle.Orderer = nullOrderer;
bundles.Add(commonStylesBundle);
}
}
Both ways have disadvantage: the injection of LESS-variables does not work in debug mode.

Categories