Returning HTML from C# functions

Topics: General, Troubleshooting
Mar 30, 2011 at 6:48 AM

Hi

I'd like to write a demo C# function to simply get first name and last name and return an HTML greeting message containing the full name. It returns string.

public static string GenerateGreeting(string firstName, string lastName)
{
     return String.Format("<div id='greeting'>Hi {0} {1}, welcome to my thoughts!</div>", firstName, lastName);
}

But when I insert this function inside a page, its return value would be HTML encoded and won't be shown as HTML.

&lt;div id='greeting'&gt Hi John Pit, welcome to my thoughts!&lt;/div&gt;
What I should do to specify the return value of a function to be treated as HTML? Is it related to return value data type?
Thanks
Mar 30, 2011 at 8:08 AM
Edited Mar 30, 2011 at 8:10 AM

Try this:

 

using System;
using System.Linq;
using System.Xml.Linq;
using Composite.Core.Xml;

namespace Test
{
  public static class InlineMethodFunction
  {
    public static XhtmlDocument GenerateGreeting(string firstName, string lastName)
    {
      string greetingString = String.Format("Hi {0} {1}, welcome to my thoughts!", firstName, lastName);
    
      XhtmlDocument document = new XhtmlDocument();
      document.Body.Add(
        new XElement(Namespaces.Xhtml+"p", greetingString));
    
      return document;
    }
  }
}

 

A few points here worth noting:

  • Composite.Core.Xml.XhtmlDocument - when this is used as the return value, your function will be available in the Visual Editor also (no need to switch to code view). 
  • XhtmlDocument is based on XDocument - part of the the System.Xml.Linq classes introduced in .NET 4, like XElement, XAttribute etc.
  • You put the html you want to have 'on screen' into the XhtmlDocument.Body node, using the Add method,
  • In case you would want to have access to the rendered page's <head /> secition and add something, you would add it it XhtmlDocument.Head
  • XML Namespaces are important - in the sample above a html <p /> element is created using Namespaces.Xhtml+"p" - the part to the left of the plus sign ensure the element is in the XHTML namespace
  • You can also return <f:function ... /> markup - and have other function fired as part of your return value.
Mar 30, 2011 at 3:30 PM

Returning XElement instead of XDocument should also be fine:

 

using System;
using System.Xml.Linq;

namespace Test
{
  public static class InlineMethodFunction
  {
    public static XElement GenerateGreeting(string firstName, string lastName)
    {
      return new XElement("p", String.Format("Hi {0} {1}, welcome to my thoughts!", firstName, lastName)));
    }
  }
}

Mar 31, 2011 at 8:05 AM

Thank you very much!

Solved my problem

Aug 11, 2011 at 12:50 PM

Hi,

I have a similar problem but a bit more complex.

My c# function is trying to embed some code from another website into the body. But It could be quite complex, you cannot wrap it into a XElement as you dont know what is coming so you have to trust what is coming.
my function looks like:

public static XhtmlDocument GetUrlContent()
    {
      var url = "urlToGetTheContent";
      var responseString = "";
      var request = WebRequest.Create(url);
     
      using (var response = request.GetResponse())   {
         if (response == null)
          {
            throw new WebException("No response");
          }

          using (var stream = response.GetResponseStream())
          {
            if (stream == null)
            {
              throw new WebException("No data returned");
            }

            using (TextReader dataReader = new StreamReader(stream))
            {
              responseString = dataReader.ReadToEnd();
            }
          } 
      }        
              
      XhtmlDocument document = new XhtmlDocument();
      document.Body.Add(responseString);
      return document;
    }

But the return is full of &lt;, &gt; There is a way to skip this escaping or any other way to  include content from a different website?
Thank you

Aug 11, 2011 at 5:16 PM
Edited Aug 15, 2011 at 12:33 PM

If function returns XhtmlDocument, you cannot return anything which isn't xml compiant.

When you add content like 

 

document.Body.Add(responseString);

 

 

it will add an xml text node, and its content gets encoded (&lt;, &gt; , ...)

The best way would be to have something like:

document.Body.Add(XElement.Parse(responseString));
But if the response isn't always XML-formatted, you can make it so with help of a dedicated tool - either Tidy.NET or HtmlAgilityPack.
Tidy.NET is used in C1, that would be the easiest way. You can find an example in 
{Website root}/Composite/Services/SourceEditor/MarkupFormatService.asmx 

        public string HtmlToXhtml(string html)
        {
            string declarations = "";
            string xhtml = "";
            int htmlElementStart = xhtml.IndexOf("<html", StringComparison.InvariantCultureIgnoreCase);
            if (htmlElementStart > 0) declarations = xhtml.Substring(0, htmlElementStart) + "\n";

            Regex tagStarterRegEx = new Regex("</?([a-zA-Z1-9]+)");
            MatchCollection tagStarterMatchCollection = tagStarterRegEx.Matches(html);
            foreach (string tagStarter in tagStarterMatchCollection.OfType<Match>().Select(f => f.Value).Distinct().OrderBy(f => f.Length))
            {
                html = html.Replace(tagStarter, tagStarter.ToLower());
            }

            byte[] htmlByteArray = Encoding.UTF8.GetBytes(html);
            using (MemoryStream inputStream = new MemoryStream(htmlByteArray))
            {
                using (MemoryStream outputStream = new MemoryStream())
                {
                    Tidy tidy = GetXhtml5ConfiguredTidy();
                    
                    TidyMessageCollection tidyMessages = new TidyMessageCollection();

                    tidy.Parse(inputStream, outputStream, tidyMessages);
                    if (tidyMessages.Errors == 0)
                    {
                        outputStream.Position = 0;
                        C1StreamReader sr = new C1StreamReader(outputStream);
                        xhtml = declarations + sr.ReadToEnd();
                    }
                }
            }

            return xhtml;
        }
Aug 12, 2011 at 9:32 AM

it works as a charm using:

 document.Body.Add(XElement.Parse(HtmlToXhtml(responseString)));
thank you napernik

Aug 12, 2011 at 6:58 PM

Great example, napernik... Excellent code sample.