Reputation: 49564
I'm looking at building a templating engine for my website. I'm using ASP.NET MVC and Linq-to-SQL.
So,
What I'm wanting to do is generate an XML document like this:
<?xml version="1.0" encoding="utf-8" ?>
<MainPage>
<MainPageHtml><![CDATA[<p>lol!</p><br/>wtf? totally!]]></MainPageHtml>
<Roles>
<Role RoleId="1">User</Role>
<Role RoleId="2">Administrator</Role>
</Roles>
</MainPage>
And transform it with XSLT like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" encoding="iso8859-1" omit-xml-declaration="yes"/><xsl:template match="/">
<xsl:value-of select="/MainPage/MainPageHtml"/>
<![CDATA[<select>]]><xsl:for-each select="//Roles/Role">
<![CDATA[<option value="]]><xsl:value-of select="@RoleId"/><![CDATA[">]]><xsl:value-of select="."/><![CDATA[</option>]]></xsl:for-each>
<![CDATA[</select>]]></xsl:template>
</xsl:stylesheet>
Using this sort of code:
XmlDocument data = new XmlDocument(); data.Load("Data.xml");
XslCompiledTransform transform = new XslCompiledTransform(); transform.Load("Transform.xslt");
StringWriter sw = new StringWriter();
transform.Transform(data, null, sw);
Console.WriteLine(sw.ToString());
Console.ReadKey(true);
Is there a quick way to turn a Linq based Model like this:
return View("MainPage", new MainPageModel
{
MainPageHtml = Config.MainPageHtml,
Roles = Config.GetAllRoles()
});
Into the model above?
Upvotes: 2
Views: 345
Reputation: 49564
Well, I found this method:
var roles = from r in db.Roles
orderby r.Name
select new XElement("Role",
new XAttribute("RoleId", r.RoleID),
r.Name);
var data = new XElement("MainPage",
new XElement("Roles", roles));
return View(data);
Which allows pretty easy creation. Is there anything better?
EDIT: I wrote an XSLT View Engine
Here is almost my whole solution:
public class XsltViewEngine : VirtualPathProviderViewEngine
{
public XsltViewEngine()
{
base.ViewLocationFormats = new string[]
{
"~/Views/{1}/{0}.xslt",
"~/Views/Shared/{0}.xslt",
};
base.PartialViewLocationFormats = base.ViewLocationFormats;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new XsltViewPage(partialPath);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return new XsltViewPage(viewPath);
}
}
class XsltViewPage : IView
{
protected string ViewPath { get; private set; }
public XsltViewPage(string viewPath)
{
this.ViewPath = viewPath;
}
public void Render(ViewContext viewContext, TextWriter writer)
{
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(viewContext.HttpContext.Server.MapPath(this.ViewPath));
object model = viewContext.ViewData.Model;
if (model == null)
{
throw new InvalidOperationException("An attempt was made to render an XSL view with a null model. This is invalid. An XSL view's model must, at very least, be an empty XML document.");
}
if(model is IXPathNavigable)
{
transform.Transform((IXPathNavigable)model, null, writer);
}
else if (model is XNode)
{
transform.Transform(((XNode)model).CreateReader(), null, writer);
}
else
{
throw new InvalidOperationException("An attempt was made to render an XSL view with an invalid model. An XSL view's model must be either an IXPathNavigable or an XNode.");
}
}
}
And, in Globals.asaxcs:
protected void Application_Start()
{
ViewEngines.Engines.Add(new XsltViewEngine());
RegisterRoutes(RouteTable.Routes);
}
Upvotes: 1
Reputation: 30714
I doubt there's a simple way to do what you're after.
What I do for a similar situation is add a ToXML() method to my data classes (the new lines aren't necessary but help when printing out the XML for diagnostic purposes so i always put them in).
public string ToXML() {
string xml =
" <provider>" + m_newLine +
" <id>" + m_id + "</id>" + m_newLine +
" <providerid>" + m_providerId + "</providerid>" + m_newLine +
" <providername>" + ProviderName + "</providername>" + m_newLine +
" </provider>";
return xml;
}
That apporach wouldn't do the whole job for you but would get you on the path.
Roles = Config.GetAllRoles();
string rolesXML = "<MainPage><MainPageHtml><p>lol!</p><br/>wtf? totally!></MainPageHtml> <Roles>"
Roles.ForEach(r => rolesXML += String.Format("<Role RoleId='{0}'>{1}</Role>", r.ID, r.Description));
string rolesXML += "</Roles>";
In your XSL you don't need the CDATA sections for new elements. Instead of:
<![CDATA[<option value="]]><xsl:value-of select="@RoleId"/><![CDATA[">]]><xsl:value-of select="."/><![CDATA[</option>]]>
you can use the much clearer:
<option>
<xsl:attribute name="value"><xsl:value-of select="@RoleId"/></xsl:attribute>
<xsl:value-of select="."/>
</option>
Upvotes: 1