Situational Background: XSD with SCH
XML Schema (XSD)
I have an XML schema definition ("the schema") that includes several other XSDs, all in the same namespace. Some of those import other XSDs from foreign namespaces. All in all, the schema declares several global elements that can be instantiated as XML documents. Let's call them Global_1, Global_2 and Global_3.
Business Rules (SCH)
The schema is augmented by a Schematron file that defines the "business rules". It defines a number of abstract rules, and each abstract rule contains a number of assertions using the data model defined via XSD. For instance:
<sch:pattern>
<sch:rule id="rule_A" abstract="true">
<sch:assert test="if (abc:a/abc:b = '123') then abc:x/abc:y = ('aaa', 'bbb', 'ccc') else true()" id="A-01">Error message</sch:assert>
<sch:assert test="not(abc:c = 'abcd' and abc:d = 'zz')" id="A-02">Some other error message</sch:assert>
</sch:rule>
<!-- (...) -->
</sch:pattern>
Each abstract rule is extended by one or more non-abstract (concrete) rule that defines a specific context in which the abstract rule's assertions are to be validated. For example:
<sch:pattern>
<!-- (...) -->
<sch:rule context="abc:Global_1/abc:x/abc:y">
<sch:extends rule="rule_A"/>
</sch:rule>
<sch:rule context="abc:Global_2/abc:j//abc:k/abc:l">
<sch:extends rule="rule_A"/>
</sch:rule>
<!-- (...) -->
</sch:pattern>
In other words, all the assertions defined within the abstract rule_A are being applied to their specific contexts.
Both "the schema" and "the business rules" are subject to change - my program gets them at run-time and I don't know their content at design-time. The only thing I can safely assume is that there are no endless recursive structures in the schema: There is always one definite leaf node for every type and no type contains itself. Put differently, there are no "infinite loops" possible in the instances.
The Problem I want To Solve
Basically, I want to evaluate programmatically if each of the defined rules is correct. Since correctness can be quite a problematic topic, here by correctness I simply mean: Each XPath used in a rule (i.e. its context and within the XQueries of its inherited assertions) is "possible", meaning it can exist according to the data model defined in the schema. If, for instance, a namespace prefix is forgotten (abc:a/b instead of abc:a/abc:b), this XPath will never return anything other than an empty node set. The same is true if one step in the XPath is accidentally omitted, or spelled wrong, etc. This is obviously not a very strong claim for "correctness" of such a rule, but it'll do for a first step.
My Approach Towards A Solution For This
At least to me it doesn't seem like a trivial problem to evaluate an XPath (not to speak of the entire XQuery!) designed for the instance of a schema against the actual schema, given how it may contain axis steps like //, ancestor::, sibling::, etc. So I decided to construct something I would call a "maximum instance": By recursively iterating through all global elements and their children (and the structure of their respective complex types etc.), I build an XML instance at run-time that contains every possible element and attribute where it would be in the normal instance, but all at once. So every optional element/attribute, every element within a choice block and so on. So, said maximum instance would look something like this:
<maximumInstance>
<Global_1>
<abc:a>
<abc:b additionalAttribute="some_fixed_value">
<abc:j/>
<abc:k/>
<abc:l/>
</abc:b>
</abc:a>
</Global_1>
<Global_2>
<abc:x>
<abc:y>
<abc:a/>
<abc:z>
<abc:l/>
</abc:z>
</abc:y>
</abc:x>
</Global_2>
<Global_3>
<!-- ... -->
</Global_3>
<!-- ... -->
</maximumInstance>
All it takes now is to iterate over all abstract rules: And for every assertion in each abstract rule it must be checked that for every context the respective abstract rule is extended by, every XPath within an assertion results in a non-empty node set when evaluated against the maximum instance.
Where I'm stuck
I have written a C# (.NET Framework 4.8) program that parses "the schema" into said "maximum instance" (which is an XDocument at run-time). It also parses the business rules into a structure that makes it easy to get each abstract rule, its assertions, and the contexts these assertions are to be validated against.
But currently, I only have each complete XQuery (just like they are in the Schematron file) which effectively creates an assertion. But I actually need to break the XQuery down into its components (I guess I'd need the abstract syntax tree) so that I would have all individual XPaths. For instance, when given the XQuery if (abc:a/abc:b = '123') then abc:x/abc:y = ('aaa', 'bbb', 'ccc') else true(), I would need to retrieve abc:a/abc:b and abc:x/abc:y.
I assume that this could be done using Saxon-HE (or maybe another Parser/Compiler currently available for C# I don't know about). Unfortunately, I have yet to understand how to make use of Saxon well enough to even find at least a valid starting point for what I want to achieve. I've been trying to use the abstract syntax tree (so I can access the respective XPaths in the XQuery) seemingly accessible via XQueryExecutable:
Processor processor = new Processor();
XQueryCompiler xqueryCompiler = processor.NewXQueryCompiler();
XQueryExecutable exe = xqueryCompiler.Compile(xquery);
var AST = exe.getUnderlyingCompiledQuery();
var st = new XDocument();
st.Add(new XElement("root"));
XdmNode node = processor.NewDocumentBuilder().Build(st.CreateReader());
AST.explain((node); // <-- this is an error!
But that doesn't get me anywhere: I don't find any properties exposed I could work with? And while VS offers me to use AST.explain(...) (which seems promising), I'm unable to figure out what to parametrize here. I tried using a XdmNode which I thought would be a Destination? But also, I am using Saxon 10 (via NuGet), while Destination seems to be from Saxon 9: net.sf.saxon.s9api.Destination?!
Does anybody who was kind enough to read through all of this have any advice for me on how to tackle this? :-) Or, maybe there's a better way to solve my problem I haven't thought of - I'm also grateful for suggestions.
TL;DR
Sorry for the wall of text! In short: I have Schematron rules that augment an XML schema with business logic. To evaluate these rules (not: validate instances against the rules!) without actual XML instances, I need to break down the XQueries which make up the Schematron's assertions into their components so that I can handle all XPaths used in them. I think it can be done with Saxon-HE, but my knowledge is too limited to even understand what a good starting point what be for that. I'm also open for suggestions regarding a possibly better approach to solve my actual problem (as described in detail above).
Thank you for taking the time to read this.
If this were an XSD schema rather than a Schematron schema, then Saxon-EE would do the job for you automatically: this is very similar what a schema-aware XQuery processor attempts to do. But another difference is that in schema-aware XQuery, you can't assume that every element named foo is a valid instance of the element declaration named foo in the schema; it's quite legitimate, for example, for a query to transform valid instances into invalid instances, or vice versa. The input and output, after all, might conform to different schemas.
Saxon uses path analysis to do this: it looks at path expressions to see "where they might lead". Path analysis is also used to assess streamability, and to support document projection (building a trimmed-down tree representation of the source document that leaves out the parts that the query cannot reach). The path analysis in Saxon is by no means complete, for example it doesn't attempt to handle recursive functions. Although all these operations require Saxon-EE, the basic path analysis code is actually present in Saxon-HE, but I would offer no guarantee that it works for any purpose other than those described.
You're basically right that this is a tough problem you've set yourself, and I wish you luck with it.
Another approach you could adopt that wouldn't involve grovelling around the Saxon internals is to convert the XQuery to XQueryX, which is an XML representation of the parse tree, and then inspect the XQueryX (presumably using XQuery) to find the parts you need.
While XQueryX (as pointed out by Michael Kay) would theoretically have been exactly what I was looking for, unfortunately I could not find anything useful regarding an implementation for .NET during my research.
So I eventually solved the whole thing by creating my own parser using the XPath3.1 grammar for ANTLR4 as an ideal starting point. This way, I am now able to retrieve a syntax tree of any Schematron rule expression, allowing me to extract each contained XPath expression (and its sub expressions) separately.
Note that another stumbling block has been the fact that .NET still (!) only handles XPath 1.0 genuinely: While my parser does everything as supposed to, for some of the found expressions .NET gave me "illegal token" errors when trying to evaluate them. Installing the XPath2 NuGet package by Chertkov/Heyenrath was the solution.
I have a curious issue for which I can not seem to find a solution on this forum (or elsewhere on the internet)
I am building a test automation application using Selenium and .NET Core 3.1. I am trying to select an element in the DOM using a OpenQA.Selenium.IWebDriver and previously selected OpenQA.Selenium.IWebElement and an Xpath selector like so:
IWebElement parent => driver.FindElement(By.CssSelector("#parent"));
IWebElement child(string t) = parent.FindElement(By.XPath($".//li[#data-original-title=\"{t}\"]"))
...
var el = child("'title-1'");
Where the DOM is the following:
<div id="parent">
... <!-- child element is nested multiple levels -->
<li title data-original-title="'title-1'"></li>
<li title data-original-title="'title-2'"></li>
...
</div>
When I try to perform this action i get the following error:
OpenQA.Selenium.NoSuchElementException : no such element: Unable to locate element: {"method":"xpath","selector":".//li[#data-original-title="'title-1'"]"}
(Session info: chrome=85.0.4183.102)
However when I copy this xpath which is apparently non existent and search the dom using chrome debugger it does match the element.
Then I tried to figure out what could be the cause, maybe I made a mistake with the parent or smth. So I try the following XPath for the child selector, matching the other attribute (with out value)
IWebElement child(string t) = parent.FindElement(By.XPath($".//li[#title]"))
This does return an element, and after checking the debugger it is my intended child element. Now I assume something must go wrong with the formatting of the string so I try the following selector (without the value)
IWebElement child(string t) = parent.FindElement(By.XPath($".//li[#data-original-title]"))
This produces (roughly) the same error as before:
OpenQA.Selenium.NoSuchElementException : no such element: Unable to locate element: {"method":"xpath","selector":".//li[#data-original-title]"}
(Session info: chrome=85.0.4183.102)
And again when I try to find the element in the DOM using the search function in the chrome debugger using the above xpath .//li[#data-original-title] it does match my expected child element.
So I am now thinking the hyphens must cause the problem. I tried some research on the issue but the only real thing I could find was this stackoverflow article
Here they recommend to use an CSS selector instead, which I tried and also did not work, furthermore I believe the relative to parent function does not work with the CSS selector.
In this article the poster comments that the problem in the end was caused by DOM structure, which does not help me very much and also makes me think I just made a big mistake somewhere along the line that I just have been overlooking.
Conclusion
Based on the above XPath selector is there anything I have done wrong that causes my mismatch, or is there maybe a difference in implementation of XPath in chrome debugger vs the .NET Core Selenium library that I should know of that could cause this issue?
Thanks in advance and sorry for the long post :$
So as it turned out it was something I overlooked. The data-original-tile attribute is set reactively, based on user mouse input (as it is used for conditionally displaying a tooltip) and title property is cleared. Since selenium only fires a click event and does not actually hover above the element without specifically clarifying so the attributes won't be swapped during automation.
Conclusion
I should have checked the source HTML more thoroughly for updates to the DOM on user interaction.
I am facing with problem when I tried to access Android element by ID in C#, I also tried with Appium-desktop version and seems not accessible for example:
I tried with and without package name and there is an exception every time which says could not find element. Also from Appium desktop selector.
AndroidElement selectPlant = driver.FindElementById("com.heidelbergcement.aom.stage.dev:id/loginForm-plantSelection")
You're misunderstanding something.
FindElementById refers to the unique identifier of the element which in most cases is the RuntimeId of the UI element.
What you should use is findElementByAccessibilityId, which refers to AutomationId of the UI element as shown in the inspect tool.
Total credit to this.
I am trying to work out how to pass a particular piece of data in a POST via a Visual Studio Web Test, without it being recognized and treated as a Context Parameter.
The POST contains a (string) body that is a json document. Part of the body includes something like the following:
"My Attribute":"Some test surrounding this {{SomeValue}} other stuff"
The issue is that the Web Test is trying to match {{SomeValue}} to a Context Parameter (which doesn't exist) and so this request fails.
The value is legitimate, and needs to be sent with the request as is.
I've done some Googling and can't find any documentation that talks about, for example, escaping this string so that the value will be passed correct, and won't be treated as a Context Parameter.
I guess I could write a Web Test plugin to intercept this particular request, and do some token replacement, but that feels like a sledgehammer approach.
Any other ideas?
You could create the context parameter SomeValue and set its value to {{SomeValue}}.
You could create the two context parameters OpenDoubleCurly and CloseDoubleCurly set to the values {{ and }} respectively. Then modify the POST text to be:
"My Attribute":"Some test surrounding this {{OpenDoubleCurly}}SomeValue{{CloseDoubleCurly}} other stuff"
Normally web tests only do one level of context parameter expansion. You need to use (or for this question avoid using) plugins that do multiple expansion passes.
I am using the roslyn API and ace text editor to create a web IDE.
When i hover over data i need it to find the symbol at the given location. This works in some situations by calling the roslyn method:
var symbol = SymbolFinder.FindSymbolAtPosition(semanticModel, offset, dotNetCodeManager.Solution.Workspace, cancellationToken);
An example of the situations where this works is when i hover my mouse over the word "table" in the below example.
var SchemaName = table.Schema.Name;
However when i hover my mouse over the word Schema or Name SymbolFinder.FindSymbolAtPosition returns null.
However:
If I go to the end of the word table and ask for autocomplete information I do get Schema in the list of recommended symbols
var result = Recommender.GetRecommendedSymbolsAtPosition(semanticModel, offset, solution.Workspace);
How do I get roslyn to find symbols that are properties, methods, or fields of objects?
So FindSymbolAtPosition should work just fine -- it's after all the same API we use for things like go to definition or any other core language feature. What I would guess here is your compilation or semantic model isn't complete, and so when we try to bind Schema or Name we for some reason. The recommendation API might be able to figure out the type of the parent and know it has members, but for some reason those members aren't properly binding.
What I would recommend you try is in your semantic model or compilation, call GetDiagnostics and verify there aren't any unexpected errors there. You might be missing a reference that's causing everything to go sideways, and clearing that will make this work fine.