XSL Conversion XML-> XML only output text?
Hi I have the following XAML which is the result of the XamlWriter.Save ():
<StackPanel Name="itemStack" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:mm="clr-namespace:MindManager;assembly=MindManager">
<mm:Item Width="Auto" Height="Auto">
<Border BorderThickness="10,10,10,10" Name="border1" Height="Auto">
<DockPanel>
<DockPanel LastChildFill="True" Name="dockPanel1" Height="33" DockPanel.Dock="Top">
<Button Name="deleteItemButton" Width="26" Height="21.638" FlowDirection="LeftToRight" DockPanel.Dock="Right" Grid.IsSharedSizeScope="False">x</Button>
<TextBox Name="tagsTextBox" Height="19">1</TextBox>
</DockPanel>
<TextBox TextWrapping="Wrap" MinLines="5" AcceptsReturn="True" AcceptsTab="True" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled" AutoWordSelection="True" Name="contentTextBox" Width="Auto" MinWidth="600" Height="Auto" MinHeight="80">2</TextBox>
</DockPanel>
</Border>
</mm:Item>
<mm:Item Width="Auto" Height="Auto">
<Border BorderThickness="10,10,10,10" Name="border1" Height="Auto">
<DockPanel>
<DockPanel LastChildFill="True" Name="dockPanel1" Height="33" DockPanel.Dock="Top">
<Button Name="deleteItemButton" Width="26" Height="21.638" FlowDirection="LeftToRight" DockPanel.Dock="Right" Grid.IsSharedSizeScope="False">x</Button>
<TextBox Name="tagsTextBox" Height="19">3</TextBox>
</DockPanel>
<TextBox TextWrapping="Wrap" MinLines="5" AcceptsReturn="True" AcceptsTab="True" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled" AutoWordSelection="True" Name="contentTextBox" Width="Auto" MinWidth="600" Height="Auto" MinHeight="80">4</TextBox>
</DockPanel>
</Border>
</mm:Item>
</StackPanel>
I want to convert it to plain XML with the following XSL:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" xmlns:mm="clr-namespace:MindManager;assembly=MindManager">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/StackPanel">
<mindcontainer>
<xsl:for-each select="mm:Item">
<minditem>
<xsl:value-of select="/tagsTextBox"/>
<xsl:value-of select="/contentTextBox"/>
</minditem>
</xsl:for-each>
</mindcontainer>
</xsl:template>
</xsl:stylesheet>
I want the result to be in the following format:
<mindcontainer>
<minditem>
content of tagsTextBox
content of contentTextBox
</minditem>
<minditem>
content of tagsTextBox
content of contentTextBox
</minditem>
</mindcontainer>
But the problem is that all I get from it is:
x
1
2
x
3
4
You can see that there is only text, no text, and the filter does not work either. x is the label of the close button that I don't want to display.
This code is used to perform the conversion:
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent=true;
StringBuilder sb = new StringBuilder();
using (XmlWriter wr = XmlWriter.Create(sb, settings))
{
XamlWriter.Save(scrollViewer1.Content, wr);
}
settings.ConformanceLevel = ConformanceLevel.Auto;
using (XmlReader rd = XmlReader.Create(new StringReader(sb.ToString())))
{
XslCompiledTransform trans = new XslCompiledTransform();
trans.Load("output.xsl");
trans.Transform(rd, XmlWriter.Create("mindstore.xms", settings));
}
a source to share
In XPath / XSLT 1.0, to select (or match) an element that is in a namespace, you must declare a namespace prefix and use that prefix in your expressions (or templates). Don't be fooled by the fact that <StackPanel>
other elements don't use prefixes in the input XML either. It uses the default namespace defined by this default namespace declaration:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Therefore, you need an appropriate declaration in your stylesheet (as for the mm namespace), except that you need to choose the namespace prefix to use. Any prefix will do (just like you didn't need to use "mm").
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...>
Then, in your stylesheet, you need to update your expressions and templates to use the prefix you chose (" x
" in my example above):
<xsl:template match="/x:StackPanel">
The reason you see the output you get is because the template rule is never called (because the name does not match). Instead, the built-in template rules are run for each of the XPath node types. For the root node and element, you just need to process the children. For text nodes, it must copy them. So the behavior you see is the same behavior that you can get with any empty stylesheet (which has no template rules): a dump of all text nodes in the input document.
It can be a pain to prefix every test name in every expression or pattern. This is why XSLT 2.0 introduced a feature called xpath-default-namespace
that allows XPath expressions and XSLT templates to use the default namespace, just like elements in XML. In this case, all you need to change is add one line:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...>
But that's only if you are using an XSLT 2.0 processor. (MSXML, for example, does not support XSLT 2.0.)
Before I leave, I see a few more problems with your stylesheet (which you would no doubt find next time):
<xsl:value-of select="/tagsTextBox"/>
<xsl:value-of select="/contentTextBox"/>
Running an expression with " /
" means "start at the document / root node". Thus, these expressions look for a document <tagsTextBox>
element and a document element <contentTextBox>
, respectively. Based on what I can see in your original source document, I think you meant to write the following:
<xsl:value-of select=".//x:TextBox[@Name = 'tagsTextBox']"/>
<xsl:value-of select=".//x:TextBox[@Name = 'contentTextBox']"/>
Hope this helps! Let me know if anything requires further explanation.
a source to share
Problems arise in XSLT, at least:
-
You need to put the matching expressions in the correct namespaces for them to match. For instance.
... xml:xaml='http://schemas.microsoft.com/winfx/2006/xaml/presentation' <xsl:template match='/xaml:StackPanel'>...
-
Expressions
value-of
(match/tagsTextBox
and/contentText
) Boxseem must match in StackPanel, but:- Also missing namespace qualification
-
These correspond to the immediate children of the document root, not the child nodes of the current content node. You probably need:
<xsl:value-of select='descendent::xaml:TextBox'/>
And maybe more.
a source to share