This questions isn't really related to Umbraco, but I'm currently building an Umbraco website in which I need to output an XPathNodeIterator for /base. First I build an xml file with XDocument and than convert it to an XPathNodeIterator. The problem is my xml file has some values stored in CDATA, but when I convert my XDocument to an XPathNodeIterator it loses the CDATA and stores the values normally.
Here is an example:
Xml in XDocument:
<medewerker>
<name>Tim Waijers</name>
<url>/nl/ons-team/tim-waijers</url>
<avatar>/media/1106/Koala.jpg</avatar>
<functie>Visual Designer</functie>
<afdeling>Design</afdeling>
<werkzaamheden>
<![CDATA[
<p>Mooi stukje <strong>html</strong>. En ook nog een linkje: <a
href="http://tweakers.net/">Tweakers</a>.</p>
]]>
</werkzaamheden>
</medewerker>
Xml in XPathNodeIterator:
<medewerker>
<name>Tim Waijers</name>
<url>/nl/ons-team/tim-waijers</url>
<avatar>/media/1106/Koala.jpg</avatar>
<functie>Visual Designer</functie>
<afdeling>Design</afdeling>
<werkzaamheden>
<p>Mooi stukje <strong>html</strong>. En ook nog een linkje: <a
href="http://tweakers.net/">Tweakers</a>.</p>
</werkzaamheden>
</medewerker>
After converting my XDocument to the XPathNodeIterator the CDATA is lost. How can I fix this?
Not sure why the XPathNodeIterator does that. It does raise the question to why are you outputting a XPathNodeIterator from /Base? Why not output the XML document?
My understanding of /Base was to enable a REST interface for Umbraco - so the output would be a text-based interpretation of that object.
If you are using the output in an XSLT, then try adding a "cdata-section-elements" to your <xsl:output> tag:
Actually, it doesn't "lose" the CDATA - rather, the XML parser does its job and generates the XML tree from the document. A CDATA Section is *only* an easier way of marking up text that's not to be parsed. It's not a node like elements and attributes...
As you can see it needs an XPathNodeIterator to output valid xml. I don't use the output in XSLT (it's being send to flash). Somehow need to find out why the XPathNodeIterator removes CDATA or find another solution how I can output xml with /base.
You should have a CreateCDATASection() method somewhere - so if you grab hold of the content from the werkzamheeden element and recreate the element something like this (pseudo code):
It seems if I return the XDocument to /base and update the config file I can output the xml with CDATA, but it seems the xml is not recognized as xml (wrong Response.ContentType?).
Still this isn't perfect as the XPathNodeIterator gives the xml output in a better format (since the browser recognizes the xml). Hope there is a better solution for this...
Try changing the "returnXml" to "true" in the config file. I think that's meaning that you are returning it as XML, rather than asking it to return as XML, (if that makes sense? Confused? I am!)
@Lee Kelleher Since I'm using /base I don't know if I can set the Response.ContentType somewhere. All I can configure is the url /base needs to call in the restExtensions.config. After that I goes directly to the /base method in which I output the XPathNodeIterator. After that I can't change the XPathNodeIterator.
@Chriztian Steinmeiner Like I said to Lee Kelleher after I return the XPathNodeIterator with /base I don't have control over it. Umbraco takes it over from there and returns the xml to the browser. I don't have a place where I can add the CDATA.
@Lee Kelleher If returnXml is true all the data is saved inside an xml. Sample if I return "this is a test". The xml output becomes <value>this is a test</value>. This is done by Umbraco. If I have returnXml to to true and I want to output my own xml I need the XPathNodeIterator. Like it says here.
Just checking, can you modify the "Digibiz2010.BLL.Base.BaseMethods.GetEmployee()" method?
If so, then yes you should be able to add the Response.ContentType = "text/xml" to it (you might need to reference System.Web and/or the HttpContext.Current?)
Set the "returnXml=true" in the config... and fingers-crossed it might just work?
After calling this code with returnXml set to false it still is returned as a text file. The ContentType is probably being reset some in the Umbraco source code.
After looking at the source code I now know how it works. Here is the part of the code in baseHttpModule.cs:
Part of the httpApp_PreRequestHandlerExecute method:
//There has to be minimum 4 parts in the url for this to work... /base/library/method/[parameter].aspx
if (urlArray.Length >= 4)
{
string extensionAlias = urlArray[2].ToString();
string methodName = urlArray[3].ToString();
httpApp.Response.ContentType = "text/xml";
restExtension myExtension = new restExtension(extensionAlias, methodName);
if (myExtension.isAllowed)
{
httpApp.Response.Output.Write(invokeMethod(myExtension, urlArray));
}
else
{
//Very static error msg...
httpApp.Response.Output.Write("<error>Extension not found or permission denied</error>");
}
//end the resposne
httpApp.Response.End();
}
Part of the invokeMethod:
/*TODO - SOMETHING ALITTLE BETTER THEN ONLY CHECK FOR XPATHNODEITERATOR OR ELSE do ToString() */
if (response != null)
{
if (myExtension.method.ReturnType.ToString() == "System.Xml.XPath.XPathNodeIterator")
return ((System.Xml.XPath.XPathNodeIterator)response).Current.OuterXml;
else
{
string strResponse = ((string)response.ToString());
if (myExtension.returnXML)
{
//do a quick "is this html?" check... if it is add CDATA...
if (strResponse.Contains("<") || strResponse.Contains(">"))
strResponse = "<![CDATA[" + strResponse + "]]>";
return "<value>" + strResponse + "</value>";
}
else
{
HttpContext.Current.Response.ContentType = "text/html";
return strResponse;
}
}
}
else
{
if (myExtension.returnXML)
return "<error>Null value returned</error>";
else
return string.Empty;
}
So the ContentType is set at a later moment again. Maybe this code should be extended to also work with the XmlDocument and XDocument and not only with the XPathNodeIterator.
Sounds like a fair suggestion. I am wondering if you would be possible to fool the current code, by creating you own class inheriting from XPathNodeIterator, and overriding the methods used. Might be worth a shot, if it is indeed possible to override them.
@Morten Bock Good idea :). I'm currently updating the baseHttpModule.cs to support XDocument and XmlDocument and will upload it as a patch to codeplex. Maybe it can become a part of 4.5.2 ;).
I've updated the source code and after testing it it seems to work :). You can now also return an XDocument and XmlDocument to output xml. Because of this the XPathNodeIterator is no longer needed which means CDATA can also be used. Here is my updated code in baseHttpModule.cs:
if (myExtension.method.ReturnType.ToString() == "System.Xml.XPath.XPathNodeIterator")
return ((System.Xml.XPath.XPathNodeIterator)response).Current.OuterXml;
else if (myExtension.method.ReturnType.ToString() == "System.Xml.XmlDocument" || myExtension.method.ReturnType.ToString() == "System.Xml.Linq.XDocument")
return response.ToString();
else
{
string strResponse = ((string)response.ToString());
if (myExtension.returnXML)
{
//do a quick "is this html?" check... if it is add CDATA...
if (strResponse.Contains("<") || strResponse.Contains(">"))
strResponse = "<![CDATA[" + strResponse + "]]>";
return "<value>" + strResponse + "</value>";
}
else
{
HttpContext.Current.Response.ContentType = "text/html";
return strResponse;
}
}
Jeroen and I discovered that this code change only works with XDocument and not XMLDocument. This hasn't been tested yet, but to get it to work with XMLDocument, you'd have to:
switch (myExtension.method.ReturnType.ToString())
{
case "System.Xml.XPath.XPathNodeIterator":
return ((System.Xml.XPath.XPathNodeIterator)response).Current.OuterXml;
case "System.Xml.XmlDocument":
return response.ToString();
case "System.Xml.Linq.XDocument":
XmlDocument xmlDoc = (XmlDocument) response;
StringWriter sw = new StringWriter();
XmlTextWriter xw = new XmlTextWriter(sw);
xmlDoc.WriteTo(xw);
return sw.ToString();
default:
string strResponse = ((string)response.ToString());
if (myExtension.returnXML) {
//do a quick "is this html?" check... if it is add CDATA...
if (strResponse.Contains("<") || strResponse.Contains(">"))
strResponse = "<![CDATA[" + strResponse + "]]>";
return "<value>" + strResponse + "</value>";
} else {
HttpContext.Current.Response.ContentType = "text/html";
return strResponse;
}
}
Haven't been able to get this to work since I'm having issues with my trunk and VS 2008.
XPathNodeIterator loses CDATA
Hello,
This questions isn't really related to Umbraco, but I'm currently building an Umbraco website in which I need to output an XPathNodeIterator for /base. First I build an xml file with XDocument and than convert it to an XPathNodeIterator. The problem is my xml file has some values stored in CDATA, but when I convert my XDocument to an XPathNodeIterator it loses the CDATA and stores the values normally.
Here is an example:
Xml in XDocument:
Xml in XPathNodeIterator:
After converting my XDocument to the XPathNodeIterator the CDATA is lost. How can I fix this?
I convert my XDocument with the following code:
Jeroen
Hi Jeroen,
Not sure why the XPathNodeIterator does that. It does raise the question to why are you outputting a XPathNodeIterator from /Base? Why not output the XML document?
My understanding of /Base was to enable a REST interface for Umbraco - so the output would be a text-based interpretation of that object.
If you are using the output in an XSLT, then try adding a "cdata-section-elements" to your <xsl:output> tag:
More info about "cdata-section-elements" here: http://www.bernzilla.com/2008/02/12/utilizing-cdata-section-elements-in-xsl/
Cheers, Lee.
Hi Jeroen,
Actually, it doesn't "lose" the CDATA - rather, the XML parser does its job and generates the XML tree from the document. A CDATA Section is *only* an easier way of marking up text that's not to be parsed. It's not a node like elements and attributes...
/Chriztian
I tried returning an XDocument with /base, but that somehow doesn't return xml, but the xml as a string. I've looked at this wiki page:
http://our.umbraco.org/wiki/reference/umbraco-base/programming-a-class-for-the-base-system
As you can see it needs an XPathNodeIterator to output valid xml. I don't use the output in XSLT (it's being send to flash). Somehow need to find out why the XPathNodeIterator removes CDATA or find another solution how I can output xml with /base.
Jeroen
@Chriztian Is there a way it can still remain the CDATA?
Could you not return the XDocument as a String? i.e. xdoc.ToString()
Set the Response.ContentType to "text/xml" ... the Flash would think its XML?
You should have a CreateCDATASection() method somewhere - so if you grab hold of the content from the werkzamheeden element and recreate the element something like this (pseudo code):
newCDATAcontent = document.createCDATASection(textNodeFromElement)
- but be aware that you might need to manually convert all the entities back to actual characters... :(
/Chriztian
It seems if I return the XDocument to /base and update the config file I can output the xml with CDATA, but it seems the xml is not recognized as xml (wrong Response.ContentType?).
Updated config:
Still this isn't perfect as the XPathNodeIterator gives the xml output in a better format (since the browser recognizes the xml). Hope there is a better solution for this...
Jeroen
Try changing the "returnXml" to "true" in the config file. I think that's meaning that you are returning it as XML, rather than asking it to return as XML, (if that makes sense? Confused? I am!)
@Lee Kelleher Since I'm using /base I don't know if I can set the Response.ContentType somewhere. All I can configure is the url /base needs to call in the restExtensions.config. After that I goes directly to the /base method in which I output the XPathNodeIterator. After that I can't change the XPathNodeIterator.
@Chriztian Steinmeiner Like I said to Lee Kelleher after I return the XPathNodeIterator with /base I don't have control over it. Umbraco takes it over from there and returns the xml to the browser. I don't have a place where I can add the CDATA.
@Lee Kelleher If returnXml is true all the data is saved inside an xml. Sample if I return "this is a test". The xml output becomes <value>this is a test</value>. This is done by Umbraco. If I have returnXml to to true and I want to output my own xml I need the XPathNodeIterator. Like it says here.
Hi Jeroen,
Just checking, can you modify the "Digibiz2010.BLL.Base.BaseMethods.GetEmployee()" method?
If so, then yes you should be able to add the Response.ContentType = "text/xml" to it (you might need to reference System.Web and/or the HttpContext.Current?)
Set the "returnXml=true" in the config... and fingers-crossed it might just work?
Cheers, Lee.
Ah, sorry - I've misunderstood how the returnXml attribute worked. Doh! Set it as false.
@Lee Kelleher I tried it and it doesn't work :(. Here is my method:
After calling this code with returnXml set to false it still is returned as a text file. The ContentType is probably being reset some in the Umbraco source code.
Jeroen
After looking at the source code I now know how it works. Here is the part of the code in baseHttpModule.cs:
Part of the httpApp_PreRequestHandlerExecute method:
Part of the invokeMethod:
So the ContentType is set at a later moment again. Maybe this code should be extended to also work with the XmlDocument and XDocument and not only with the XPathNodeIterator.
Jeroen
Sounds like a fair suggestion. I am wondering if you would be possible to fool the current code, by creating you own class inheriting from XPathNodeIterator, and overriding the methods used. Might be worth a shot, if it is indeed possible to override them.
@Morten Bock Good idea :). I'm currently updating the baseHttpModule.cs to support XDocument and XmlDocument and will upload it as a patch to codeplex. Maybe it can become a part of 4.5.2 ;).
Jeroen
I've updated the source code and after testing it it seems to work :). You can now also return an XDocument and XmlDocument to output xml. Because of this the XPathNodeIterator is no longer needed which means CDATA can also be used. Here is my updated code in baseHttpModule.cs:
The patch can be found at http://umbraco.codeplex.com/SourceControl/PatchList.aspx and has ID 6562.
Jeroen
Since the patch still hasn't been evaluated yet I created a workitem for it: http://umbraco.codeplex.com/workitem/28923. Please vote. Thanks.
Jeroen
Jeroen and I discovered that this code change only works with XDocument and not XMLDocument. This hasn't been tested yet, but to get it to work with XMLDocument, you'd have to:
Haven't been able to get this to work since I'm having issues with my trunk and VS 2008.
Hi Chad,
In your example you've switched the xmlDocument and XDocument code. It should be:
Jeroen
D'oh! That's what I get for not being able to test it.
I've uploaded a new patch (7534) in which both the XDocument and XmlDocument work!
Jeroen
Awesome. Thanks!
The patch has already been applied so it will be part of Umbraco 4.6 :).
Jeroen
is working on a reply...