Archive

Posts Tagged ‘xslt’

XPath2 in .Net

September 17, 2009 3 comments

“What? No XPath2 in .Net? Only v1?”
This is how it starts. At first you can call Microsoft however you want for not implementing XPath2, not even in .Net framework 4. But then you should come together and do it yourself. Actually doing a small workaround, that will enable you to use XPath2 functions in .Net.
First of all see the difference between the XPath functions implemented by Microsoft and the XPath2 standard functions.
Most likely for XSLT transformation in .Net you will use XslCompiledTransform. The workaround is based on adding an extension object.

// creating the XSLT transformation object
XslCompiledTransform xslTransform = new XslCompiledTransform();
// loading the stylesheet - see the Load method for all the possible arguments
xslTransform.Load(...);
// create the transformation arguments object
XsltArgumentList xslArg = new XsltArgumentList();
// the extension object needed for the workaround
xslArg.AddExtensionObject("urn:xpath2", new XPath2());
// apply the transformation
xslTransform.Transform(inputXmlDocument, xslArg, output);

And now we have to define the XPath class

using System.Text.RegularExpressions;
public class XPath2
{
	public int compare(String comparand1, String comparand2)
	{
		return comparand1.CompareTo(comparand2);
	}

	public String replace(String input, String pattern, String replacement)
	{
		return Regex.Replace(input, pattern, replacement);
	}
}

For the sake of example and simplicity I included in here only two example functions, compare and replace. But you can implement here all the XPath2 functions that you need, as methods (not static ones) of the class XPath2.

In the XSLT file it is mandatory to have

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xpath2="urn:xpath2">

as the first line. And then using any function will be simple and straightforward:

<xsl:value-of select="xpath2:compare('xpath1', 'xpath2')" />

Of course another way will be to simply use Saxon, an open source implementation for XSLT 2 for both Java and .Net. But if you just need an XPath2 function, it should be simpler what I described above.

Advertisements
Categories: Software Tags: , , ,

Mobile web

January 10, 2009 3 comments

Mobile web is a new trend and a website for mobile devices is more like a must have these days. It is more likely that your company has a web site, but if you access it with a mobile device, the user experience could be unpleasant.

It is not easy to create a site for mobile devices and it is definitely hard to maintain. And not only because of the huge amount of mobile devices, but also because you have to keep it in sync with the normal one. Following I’ll give you a few pointers.

But first you have to clearly define your mobile web presence. Sometimes it doesn’t even make sense to invest time and effort into it. Let’s say that your solely company product is an FTP server. Definitely you won’t expect to sell it to mobile users. So your entire mobile website can consist of a page stating the company name and logo, product name and description, the link where you can find out more information by browsing with a non-mobile web browser. The best part is that you don’t have to update it and it could remain like this for many years. And that could be the case with many other products for which the target user is an enterprise one, with required access from a non-mobile web browser, like a SDK, online 3D highly graphical game etc.

On the other side of the coin, is when your company product is an exclusively mobile product, like a game for mobile devices, ring tones etc. Then you can simply have only a mobile web site. Of course the desktop users won’t an experience up to their device capabilities, but they aren’t actually your target and your site is not of any real use to them.

And there is the case where your presence has to be strong in both worlds. Following I will concentrate more on this, but most of the below pointers apply to the above cases as well.

  1. CSS. First of all, your web site has to be CSS-based. Forget about tables, use DIVs. Move all CSS rules into external files. Keep the HTML as clean, simple and free of layout settings as possible. Then supporting a mobile device will be as simply as defining a CSS file.
    You can include a CSS in a page and have it active only for mobile devices using the handheld value for the media attribute of the link tag. Even tough this is very convenient, I wouldn’t recommend to be the only thing to rely on. I would define a series of CSS files for each device or set of devices. So, you would include in your HTML the following lines:

    <link rel='stylesheet' type='text/css' href='desktop.css' media='screen'/>
    <link rel='stylesheet' type='text/css' href='[mobile].css' media='handheld'/>
    <link rel='stylesheet' type='text/css' href='print.css' media='print'/>
    

    [mobile] should be dynamically generated and it should point to a file corresponding to the user mobile device. Notice also that I didn’t forget about the printable version :). The CSS media target can be specified also with

    <style type='text/css' href='[mobile].css' media='handheld'>
    </style>
    

    or directly in the CSS

    @media handheld {
        /* mobile specific CSS rules */
    }
    

    If you want to split the devices and optimize for groups, one important criteria could be the screen resolution.

    Even tough this is a very clean approach, as I already said, I would not recommend this to be the only one, especially for very large and complex websites. The mobile website should be a stripped down version, both in terms of layout (most important) and content.

  2. XHTML. Use XHTML instead of HTML. It is better structured and much easier to maintain and to automatically transform.
  3. Dynamically generated content. Even after using CSS for layout, you still have to have different HTML code, then consider generating automatically on the server the specific parts inside the page. For performance purposes you should have in mind using some kind of cache for those automatically generated parts.
    If your entire website is automatically generated, like a portal or an e-commerce website, then you could have different layouts. But don’t fall into having too many device specific code. That should be maintained as well.
  4. Transcoded content. It is the case when you already have a website, specifically a larger one, more likely on the non-mobile web, and you want to reach the mobile users as well. You should consider implementing an automatic translation tool between the two layouts. Of course, here a CSS based website will make your life much more easier. XHTML too.
    As a translation tool, I would recommend XSLT. If your code is HTML, then you have to convert it first to XHTML.
    Don’t forget to resize the images too. Many devices supports images only up to a certain file size, width or height.
    This is a fast and cheap solution, but probably not the best one. Also take into account here the size of your website, otherwise the translation tool could become more expensive then recreating your entire website. And remember that it comes to a cost in performance.
  5. Real life testing. Consider using for testing real mobile devices, not emulators. Use mobile devices with different screen resolution, operating systems, browsers. Don’t forget about touch screens either.
  6. Performance. Test for performance here too, but be careful how you define it. The bandwidth here is different: smaller pages, smaller images, different number of users etc.

If you’re interested in recognizing user’s mobile device on the server side, see my other article: Mobile device recognition.

I hope these will help you in outlining your mobile web personality, but as I said before, first take a step back and think of what exactly you need, want and invest in this.
The mobile web is for sure a different, but thrilling experience.

Categories: Web Tags: , , ,

Mobile device recognition

October 10, 2008 29 comments

Nowadays when you create a web site you should also think to the ones accessing it from a mobile device. But how you can know this?
I’m going to show you next a few methods on how you can do this and present the advantages and disadvantage of each one.

WURFL

My favorite method of detecting a mobile device is WURFL. WURFL is an open source database of mobile devices capabilities. Every mobile device is identified by its User-Agent string (you can find it in the HTTP headers) and has associated a list of capabilities grouped into categories. How exactly you can use it in .NET you can see here.
You will also have to consult the X-Device-User-Agent HTTP header because some mobile providers are rewriting the User Agent. And just to be sure that you also recognize Opera Mini (and actually the real device) you can inspect the information in the X-OperaMini-Phone-UA header.
More on the User-Agent HTTP header you can find out on W3C (section 14.43).
I personally like WURFL because it is open source, it tells you more than if the device is mobile or not. The downside is that probably it does not contain info about all the latest mobile devices. But you can easily become a WURFL contributor and submit these info yourself into the WURFL database. Before doing these you should familiarize very well with the WURFL documentation.
I also like that the community is very active and the database contains approximately 7000 devices, making it a very effective method and most importantly with no false positives, unlike the next methods.
Moreover, WURFL has a library for accessing the XML database implemented in Java, .NET and PHP.

UAProf

If WURFL is a devices database, UAProf (User Agent Profile) is a capabilities database for a single device.
The mobile browser could contain an HTTP header usually named x-wap-profile and its value is an URL pointing to the profile described in RDF (see UAProf specs as PDF).
There are some serious downsides to this method:

  • The name of the HTTP header is not always x-wap-profile. It could also be wap-profile or xx-profile, where xx is a number. The Opt header can contain this number.
  • There is no standard regarding the capabilities naming and grouping
  • Not all the devices have UAProf
  • Not all the UAProfs are still available
  • Retrieving and parsing the UAProf can be quite time consuming

Altough you can use an UAProf to define a device in the WURFL database, which should be the preferred way.

Browser.isMobile (.NET dependent)

This is the method that I recommend the less because it is .NET dependent, not quite up-to-date and it doesn’t offer too many information about the device capabilities. Anyway for your reference I include it as well.

if (HttpContext.Current.Request.Browser["IsMobileDevice"] == "true") 
{
    // the browser IS a mobile device
}
else
{
    // the browser IS NOT a mobile device
}

.

Next steps

What you do next, after knowing with each type of device are you dealing with, it’s a different story. For sure I wouldn’t recommend you to create two versions of the same page. The best way will be to have only one version and to customize the layout using CSS. Then you definitely have to avoid the “evil” HTML table to build your layout. A lot of current mobile browser don’t handle very good nested tables. Here a method of finding out also the mobile device capabilities (such as screen width and height) will help you in building a better layout. Altough recommended will be to use percentages instead of fixed sizes.

Another way will be to use XSLT to transform the desktop version into the mobile one. Please keep in mind that the transformation should be made on the server side, as almost certain the device will not have this capacity. This is a good approach and you can even transform the page into WML (or even, why not, VoiceXML) but you will have to sacrifice some performance on the server side. This is acceptable as the mobile device version traffic has a lower bandwidth. I already wrote an XSLT server side transformer in .NET and should be as simple and straightforward to implement it in Java as well.

In conclusion, I would recommend to use a combination of the above methods for detecting mobile devices and creating mobile web pages.

Later edit: If you’re also interested in how you can test your mobile web pages please see the next article Testing web mobile pages.

Categories: Software, Web Tags: , ,

XSLT handler in ASP.net

September 4, 2008 4 comments

Next I will explain you a solution that may come handy to you. First of all, the problem. Quite simple: you have some HTML pages, that you would like to include them in your pages, but in a different format/layout. Unfortunately you cannot modify the pages as you’re not the author. Just to understand better, let’s say that you want to include some Wikipedia articles in your pages. But they have to match with your own layout and you want only some sections of them.
So you have to transform an HTML into another. This sounds pretty much like XSLT and this is what we will use. It will be your task to write the XSLT. I’m only going to tell you how to integrate in your web application.

For the sake of simplicity and reuse, we will write an ASP.net handler for XSLT file that it will take the XSLT file and receive as a parameter the XML/HTML/XHTML URL and it will output the transformed result.

Find below the code for it:

public class XSLTHandler : IHttpHandler
{
    // process the request
    public void ProcessRequest(HttpContext context)
    {
        // the XSLT argument list
        XsltArgumentList xslArg = new XsltArgumentList();

        String url = context.Request.QueryString.Get("u");
        // if no URL, either special or from u parameter then send a NOT_FOUND error
        if (url == null)
        {
            send(context.Response, HttpStatusCode.NotFound);
            return;
        }

        // adding the URL as an XSLT parameter
        xslArg.AddParam("url", "",  url);

        try
        {
            // load the XML document at the specified URL
            XmlDocument xmlDoc = loadXMLDocument(url,
                    "POST".Equals(context.Request.HttpMethod)
                    ? context.Request.Form.ToString() : null);

            // load xsl
            String xslPath = context.Request.MapPath(context.Request.Url.LocalPath);

            // try to get the compiled XSLT from cache
            XslCompiledTransform xslTransform = (XslCompiledTransform)context.Cache.Get(xslPath);
            if (xslTransform == null)
            {
                // if the XSLT file doesn't exist, send a 404
                if (!File.Exists(xslPath))
                {
                    send(context.Response, HttpStatusCode.NotFound);
                    return;
                }

                xslTransform.Load(xslPath);

                // put the XSLT into cache
                context.Cache.Insert(xslPath, xslTransform, new CacheDependency(xslPath));
            }

            // transform
            context.Response.ContentType = "text/html";
            context.Response.Clear();
            xslTransform.Transform(xmlDoc, xslArg, context.Response.Output);

        }
        catch (HttpException exc)
        {
            if (exc.GetHttpCode() == (int) HttpStatusCode.Redirect)
            {
                // if it is to be redirected, redirect to the url
                context.Response.Redirect(url);
            }
            else
            {
                // if any other error, then send the error code
                send(context.Response, (HttpStatusCode)exc.GetHttpCode());
            }
        }
        catch (Exception)
        {
            // if other error send an internal server error response
            send(context.Response, HttpStatusCode.InternalServerError);
        }
    }

    // IHttpHandler method
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }

    private void send(HttpResponse Response, HttpStatusCode StatusCode)
    {
        Response.Clear();
        Response.StatusCode = (int)StatusCode;
        Response.StatusDescription = StatusCode.ToString();
        Response.End();
    }

    /// 
    /// Load and returns an XML document from the given url.
    /// 
    /// the url where is the XML content
    /// if not null the request will be a POST request 
    /// and the parameters are sent along with the request; if null then the request method is GET
    /// the loaded XML document
    public static XmlDocument loadXMLDocument(String url, String postParams)
    {
        Object reader;
        if (url.StartsWith("/") || (!url.Contains(Uri.SchemeDelimiter)))
        {
            // if it is a local request then use the internal execute instead of making a web request
            // also the form and query string parameters are preserved
            StringWriter writer = new StringWriter();
            HttpContext.Current.Server.Execute(url, writer, true);
            reader = new StringReader(writer.ToString());
        }
        else
        {
            // create webrequest
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Timeout = RequestTimeout;
            // you can also add a proxy
            //request.Proxy = WebProxy;
            if (postParameters != null)
            {
                // Set values for the request back
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = postParameters.Length;

                // Write the request
                StreamWriter stOut = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII);
                stOut.Write(postParameters);
                stOut.Close(); 
            }

            // get the response of the request
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            // if something went wrong don't try to parse the document
            if (response.StatusCode != HttpStatusCode.OK)
                throw new HttpException((int)response.StatusCode, response.StatusDescription);

            if (response.ContentType.StartsWith("text/html"))
            {
                // if it is HTML transform it with SgmlReader
                // create instance of SGMLReader and change settings
                SgmlReader sgmlReader = new SgmlReader();
                sgmlReader.WebProxy = Configuration.Proxy;
                sgmlReader.DocType = "HTML";
                sgmlReader.CaseFolding = CaseFolding.ToLower;
                sgmlReader.InputStream = new StreamReader(response.GetResponseStream(), Encoding.UTF8, true);

                reader = sgmlReader;
            }
            else if (response.ContentType.StartsWith("text/xml"))
            {
                // if not html simply get it from the response
                reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8, true);
            }
            else
            {
                // redirect to the normal page, as it is nor HTML nor XML
                throw new HttpException((int)HttpStatusCode.Redirect, url);
            }
        }
        // load the XML document
        XmlDocument xmlDoc = new XmlDocument();
        if (reader is TextReader)
            xmlDoc.Load((TextReader)reader);
        else if (reader is XmlReader)
            xmlDoc.Load((XmlReader)reader);
        return xmlDoc;
    }
}    

To explain shortly what happens: when a file like t.xslt is requested, the XSLTHandler will process the request. First it will load and compile the XSLT file using XSLTCompiledTransform, trying first to load the XSLT compiled object from cache.
Next step is to retrieve the XML/XHTML/HTML content from the URL given as the url parameter. If the content is HTML, then it will be transformed using an SGML parser into XHTML, so it can be transformed with XSLT. Then the actual processing takes place and the result is generated into the response output.

Of course the above code can be extended. You can put in the app configuration if you want to use the cache, what proxy to use, if you want to allow only URLs from some hosts to be parsed and so on. Also other parameters can be added as XSLT arguments: the URL of the XSLT file, the HTTP headers, the request parameters etc.

You saw in the code references to an SgmlReader and I also told you above about feeding the HTML content first into an SGML parser. But how to do it? The SgmlReader is already implemented and you can download it from here and use it like an XmlReader.

Last step is to configure the handler into the Web.config, by simply adding the handler to process *.xsl files:

    <httpHandlers>
        <add verb="*" path="*.xsl" type="XSLTHandler" />
    </httpHandlers>

That’s it!

Categories: Software, Web Tags: , , ,