开发者

System.Xml.XmlDocument, System.Xml.XPath.XPathNavigator{ReplaceSelf,OuterXml}: Replacing Placeholders In Document

开发者 https://www.devze.com 2023-03-03 13:35 出处:网络
I\'m currently maintaining a Web application that I wrote about a year or so ago for work. Basically it allows users to create custom XHTML report templates. I use XmlDocument, XPathNavigator, et. al.

I'm currently maintaining a Web application that I wrote about a year or so ago for work. Basically it allows users to create custom XHTML report templates. I use XmlDocument, XPathNavigator, et. al. to validate, sanitize, and substitute placeholders with data.

It works fine for the most part, but I've just realized that I haven't been actually substituting the placeholder elements with the data, but rather replacing the text of the placeholder with the data text.

Contrived example:

<span class="my-placeholder">{CompanyName}</span>

Would become...

<span class="my-placeholder">Castopulence</span>

This wasn't really a problem in the past because the templates were only ever processed one time; when the user opted to run it against a set of selected data. Now, however, we're offering them the option to preview the resulting output and modify it before printing. The result of this is开发者_如何学运维 that it gets processed more than once by the substitution engine: first to generate the original output, and again when the modified output is "printed". The substitution engine finds a placeholder that is actually data, fails to find the corresponding data element to substitute, and the process fails with an exception.

The original code used the XPathNavigator.InnerXml property to change the contents of the placeholder element from "{CompanyName}" to "Castopulence" (for example):

placeholder.InnerXml = this.Server.HtmlEncode(value);

So it seems reasonable that if I wanted to outright replace this placeholder element, I would instead use XPathNavigator.OuterXml:

placeholder.OuterXml = this.Server.HtmlEncode(value);

This does seem to work in most cases, or at least I assume it does, but a seemingly random substitution always causes a System.InvalidOperationException to be thrown with the message, "No content generated as the result of the operation." So I'm not really sure any of them are working, but I assume they are since no exception is thrown for previous substitutions.

I can't really figure out what this means, and Google only has 4 results for the exact phrase of the exception message, each in a language that I don't speak. Google translating them revealed nothing relevant.

Experimentally I tried XPathNavigator.ReplaceSelf, which seems to accomplish the same thing, but unfortunately the same exception is thrown from the same inner call.

Stack trace in both cases:

at System.Xml.DocumentXmlWriter.Close(WriteState currentState)

at System.Xml.XmlWellFormedWriter.Close()

at System.Xml.XPath.XPathNavigator.ReplaceSelf(XmlReader newNode)

at System.Xml.XPath.XPathNavigator.ReplaceSelf(String newNode)

...Snipped private application symbols...

The MSDN reference for ReplaceSelf describes the exceptions that it throws, but the only System.InvalidOperationException exception thrown is for when "The XPathNavigator is not positioned on an element, text, processing instruction, or comment node." From the Visual Studio debugger, I can confirm that it is positioned on an element. My code agrees. The XPathNavigator's pointing to the placeholders are actually clones of System.Xml.XPath.XPathNodeIterator.Current (all of which are added to a list, which is converted to an array, and iterated over during the substitution phase).

What is wrong (what does the exception actually mean) and how do I fix it?

Append: The class that is actually throwing the exception (System.Xml.DocumentXmlWriter) doesn't seem to be documented on MSDN (or at least, I couldn't find it with Google Search nor MSDN search).

Append: I've determined that the data for the one substitution causing the problem is just a single-space (i.e., " "). I don't understand why this could be a problem for the XPathNavigator, but apparently it is... Seems an empty string (i.e., "") is too. Perhaps the problem then is trying to replace an element with only whitespace. I don't see why that should be a problem though.


I am also getting the same error message:

No content generated as the result of the operation

XmlWriter writer = replaceTextIterator.Current.ReplaceRange(replaceTextIterator.Current);

XPathNodeIterator iterator5 = replaceTextIterator.Current.SelectChildren(XPathNodeType.All);
while (iterator5.MoveNext())
{
  writer.WriteNode(iterator5.Current, true);
}

writer.Close();

If iterator.count = 0, I get that error message, so don't close xmlwriter object without writing any node.


This isn't an answer to your question (which is quite old anyway), but for people that came here looking for the cause of No content generated as the result of the operation. that occurs when setting the value of InnerXml on a leaf node, you might see success using SetValue instead.

Why it matters for a leaf node, I can't tell you for sure. I would guess it's an error from trying to parse nodes out a simple string.

See more information about the difference between Value and InnerXml in this stackoverflow question, or in this InfoPathDev question (in my opinion this one is more helpful).

So if I have an XPathNavigator positioned at this node:

<my:group2 my:field4="Hi">
    <my:field1>Hello</my:field1>
    <my:field2 my:field5="yay">Goodbye</my:field2>
    <my:field3></my:field3>
</my:group2>

InnerXml would be:

        <my:field1>Hello</my:field1>
        <my:field2 my:field5="yay">Goodbye</my:field2>
        <my:field3></my:field3>

And Value would be:

        Hello
        Goodbye

For leaf nodes, InnerXml and Value should typically be the same, but I'd suggest using Value for getting a node's value as that's what it's intended for.
0

精彩评论

暂无评论...
验证码 换一张
取 消