Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 11:58
    Jeroen Breuer
    0

    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:

    <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>
    &lt;p&gt;Mooi stukje &lt;strong&gt;html&lt;/strong&gt;. En ook nog een linkje: &lt;a
    href="http://tweakers.net/"&gt;Tweakers&lt;/a&gt;.&lt;/p&gt;
        </werkzaamheden>
    </medewerker>

    After converting my XDocument to the XPathNodeIterator the CDATA is lost. How can I fix this?

    I convert my XDocument with the following code:

    xmlDocument.CreateNavigator().Select("/")

    Jeroen

  • Lee Kelleher 4026 posts 15836 karma points MVP 13x admin c-trib
    Aug 25, 2010 @ 12:20
    Lee Kelleher
    0

    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:

    <xsl:output ... cdata-section-elements="werkzaamheden" />

    More info about "cdata-section-elements" here: http://www.bernzilla.com/2008/02/12/utilizing-cdata-section-elements-in-xsl/

    Cheers, Lee.

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Aug 25, 2010 @ 12:38
    Chriztian Steinmeier
    1

    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 

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 12:42
    Jeroen Breuer
    0

    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

     

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 12:46
    Jeroen Breuer
    0

    @Chriztian Is there a way it can still remain the CDATA?

  • Lee Kelleher 4026 posts 15836 karma points MVP 13x admin c-trib
    Aug 25, 2010 @ 13:07
    Lee Kelleher
    0

    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?

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Aug 25, 2010 @ 13:08
    Chriztian Steinmeier
    0

    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

     

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 13:10
    Jeroen Breuer
    0

    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:

    <ext assembly="Digibiz2010.BLL" type="Digibiz2010.BLL.Base.BaseMethods" alias="Digibiz">
        <permission method="GetEmployee" allowAll="true" returnXml="false" />
      </ext>

    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

  • Lee Kelleher 4026 posts 15836 karma points MVP 13x admin c-trib
    Aug 25, 2010 @ 13:18
    Lee Kelleher
    0

    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!)

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 13:21
    Jeroen Breuer
    0

    @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.

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 13:23
    Jeroen Breuer
    0

    @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.

  • Lee Kelleher 4026 posts 15836 karma points MVP 13x admin c-trib
    Aug 25, 2010 @ 13:24
    Lee Kelleher
    0

    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.

  • Lee Kelleher 4026 posts 15836 karma points MVP 13x admin c-trib
    Aug 25, 2010 @ 13:26
    Lee Kelleher
    0

    Ah, sorry - I've misunderstood how the returnXml attribute worked. Doh!  Set it as false.

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 13:29
    Jeroen Breuer
    0

    @Lee Kelleher I tried it and it doesn't work :(. Here is my method:

    public static XDocument GetEmployee(string language)
            {
                HttpContext.Current.Response.ContentType = "text/xml";
                return XmlController.GetEmployee(language);
            }

    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

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 13:40
    Jeroen Breuer
    0

    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.

    Jeroen

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    Aug 25, 2010 @ 13:56
    Morten Bock
    1

    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.

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 14:07
    Jeroen Breuer
    0

    @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

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Aug 25, 2010 @ 14:36
    Jeroen Breuer
    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;
        }
    }

    The patch can be found at http://umbraco.codeplex.com/SourceControl/PatchList.aspx and has ID 6562.

    Jeroen

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Sep 20, 2010 @ 09:30
    Jeroen Breuer
    0

    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

  • Chad Rosenthal 272 posts 474 karma points
    Oct 16, 2010 @ 16:59
    Chad Rosenthal
    0

    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.

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Oct 16, 2010 @ 17:11
    Jeroen Breuer
    1

    Hi Chad,

    In your example you've switched the xmlDocument and XDocument code. It should be:

    case "System.Xml.XmlDocument":
        XmlDocument xmlDoc = (XmlDocument) response;
        StringWriter sw = new StringWriter();
        XmlTextWriter xw = new XmlTextWriter(sw);
        xmlDoc.WriteTo(xw);
        return sw.ToString();
    case "System.Xml.Linq.XDocument":
        return response.ToString();

    Jeroen

  • Chad Rosenthal 272 posts 474 karma points
    Oct 16, 2010 @ 17:13
    Chad Rosenthal
    0

    D'oh! That's what I get for not being able to test it.

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Nov 25, 2010 @ 15:29
    Jeroen Breuer
    2

    I've uploaded a new patch (7534) in which both the XDocument and XmlDocument work!

    Jeroen

  • Chad Rosenthal 272 posts 474 karma points
    Nov 29, 2010 @ 14:16
    Chad Rosenthal
    0

    Awesome. Thanks!

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Nov 29, 2010 @ 14:21
    Jeroen Breuer
    0

    The patch has already been applied so it will be part of Umbraco 4.6 :).

    Jeroen

Please Sign in or register to post replies

Write your reply to:

Draft