使用XML创建HTML表单来定义布局C#

时间:2011-10-26 10:27:56

标签: c# xml .net-3.5

我在以下架构中有一些XML:

<Form ID="1" Formtitle="Title">
    <Fields>
            <Fieldset Legend="LegendText" >
                <Field FieldName="Field1" Label="Title" Type="Text" Required="1" />
                <Field FieldName="Field2" Label="Radio" Type="Radio" Required="0">
                    <Option Value="1" Text="Just One"/>
                    <Option Value="2" Text="Maybe Two"/>
                </Field>
            </Fieldset>
    </Fields>
</Form>

我需要在C#中解析这个以生成一个代表以下内容的HTML表单:

<h1>Formtitle</h1>
<form id="1" action="myurl.com">
    <fieldset>
        <legend>LegendText</legend>
        <label>Title</label>
        <input type="text" name="Field1" class="jqueryValidate"/>
        <!-- jqueryvalidate class added as required is equal to 1 in XML -->
        <label>Radio</label>
        <input type="radio" name="Field2" Value="1"/> Just One
        <input type="radio" name="Field2" Value="2"/> Maybe Two
    </fieldset>
</form>

现在,我知道我可以使用XSLT实现同样的功能,但是我必须在这里使用C#,因为我将把它包装到一个控件中,我可以放入任何一个页面。

我的问题是,我怎么能这样做?我设想它需要某种类型的嵌套switch语句来检查节点名称和类型等,并构造HTML。但是,我希望情况并非如此,你们的boffins可以帮助我指出正确的方向。

提前致谢:)

戴夫

3 个答案:

答案 0 :(得分:0)

很抱歉确认您的担忧,但没有“通用”或“简单”的方法来执行此操作。

正如您所提到的,XSLT是标准做法,如果失败,您需要构建某种自定义解析器/字符串构建器,它会执行相同的操作。

我们想到的一种方法是使用MVC框架来减轻您的工作量。

MVC框架带有'autobinder'之类的功能,基本上可以接受任何类并生成适当的HTML(使用哪种视图引擎)。

因此,如果您选择了该路由,您的任务将仅限于简单解析XML并创建格式良好的注释装饰类。然后,MVC框架将能够以HTML格式为您生成所有CRUD视图。

希望有所帮助,干杯。

答案 1 :(得分:0)

实现此目的的一种方法是设计一些表示您要生成的各种HTML实体的对象。请注意,这是一个非常非常简化的示例。你可以拥有一个基类,里面有各种各样的技巧和可重用性。但这超出了问题的范围,我们都会在这里整天待在这里:)

public interface IElement
{
    string RenderStart();
    string RenderEnd();
    string Render();
    IList<IElement> Children { get; }
    void LoadFromXML(XmLReader reader);
}  // eo interface IElement


public abstract class Element : IElement
{
    List<IElement> children_ = new List<IElement>();

    public List<IElement> Children { get { return children_; } }

    public string Render()
    {
        StringBuilder builder = new StringBuilder(RenderStartTag());
        foreach(IElement e in children_)
            builder.Append(e.Render());
        builder.Append(RenderEndTag());
        return builder.ToString();
    }
}  // eo class Element


public class FieldSetElement : Element
{
    public string RenderStart()
    {
        StringBuilder builder = new StringBuilder();
        builder.Append("<fieldset legend=\"")
               .Append("\">");
        return builder.ToString();
    }

    public string RenderEnd()
    {
        return ("</fieldset>");
    }

    public string Legend {get; set; }

    public void LoadFromXml(XmlReader reader)
    {
        Legend = reader.GetAttribute("legend");
    } // eo LoadFromXml
}  // eo class FieldSet

显然,每个HTML元素都有一个Element派生类。我们还有一种方法可以从Xml节点初始化它们。但是现在,我们需要将我们的Xml标签与我们的DOM对象相关联。我们可以使用字典来做到这一点(在这个简单的例子中。我更喜欢一个完整的工厂,但是再次 - 这超出了这个答案的范围!)

public delegate IElement ElementCreator;
Dictionary<string, ElementCreator> creators_ = new Dictionary<string, ElementCreator>

我会继续为FieldSet元素添加一个:

creators_["fieldset"] = () => { return new FieldSet(); };
// and so on for other creators and elements.

确定!我们快到了。下一步是将该XML文档转换为对象。我想在这一点上我们会有一些根对象。我打电话给Html(令人惊讶的名字!)。你可以猜到,它也来自Element。它将作为我们的根节点。

Html root = new Html();

现在,我们需要一个函数来读取Xml并将其转换为我们当前的父级。

XmlReader reader = new XmlReader("layout.xml");
Stack<IElement> currentRoot = new Stack<IElement>();
currentRoot.Push(root);
while(reader.Read())
{
    // get the element tag
    if(reader.NodeType == XmlNodeType.Element)
    {
       Debug.Assert(creators_.ContainsKey(reader.Name));  // must have it!
       IElement newElement = creators_[reader.Name].Invoke(); // create it
       newElement.LoadFromXml(reader);            // tell element to read itself in
       currentRoot.Peek().Children.Add(newElement); // add to parent
       currentRoot.Push(newElement);  // we are now new parent
    }
    else if(reader.NodeType == XmlNodeType.EndElement)
       currentRoot.Pop();  // just pop it off!
}

哇!我们几乎完成了。此时,我们有一个对象集合!剩下的就是渲染它们。

string html = root.Render();

BAM。我们已经完成了。

很抱歉,如果代码中有任何错误,我会尝试重复一遍。我没有跑过这个,但我之前做过类似的事。我也不是说这是最好的方式,我相信还有其他方法。这是一种方式。出于简洁的原因,我遗漏了属性的渲染,但我很乐意添加另一个具体的Element示例来说明这可能有用。

答案 2 :(得分:0)

我不知道您对使用XSLT有多厌恶,但您可以通过在C#中使用XSLT实现此目的。

using System.Xml.Xsl;

namespace XmlTransform
{
    class Program
    {
        static void Main(string[] args)
        {
            // Load the style sheet.
            XslCompiledTransform xslt = new XslCompiledTransform();
            xslt.Load(@"C:\style.xsl");

            // Execute the transform and output the results to a file.
            xslt.Transform(@"C:\input.xml", @"C:\result.html");
            Console.WriteLine("Result saved to C:\result.html");
            Console.ReadLine();
        }
    }
}

input.xml文件是您在原始问题中声明的内容,而style.xsl看起来像这样

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/Form">
<html>
<body>
<h1><xsl:value-of select="@Formtitle"/></h1>
<form id="1" action="myurl.com">
<fieldset>
    <legend><xsl:value-of select="Fields/Fieldset/@Legend"/></legend>
<label>Title</label>
<xsl:for-each select="Fields/Fieldset/Field">
<xsl:if test="@Type = 'Text'">      
<xsl:if test="@Required='1'">
<input type="text" name="{@FieldName}" class="jqueryValidate"/>
</xsl:if>
<xsl:if test="@Required='0'">
<input type="text" name="{@FieldName}"/>
</xsl:if>
</xsl:if>

<xsl:if test="@Type = 'Radio'">
<xsl:if test="@Required='1'">
<label><xsl:value-of select="@Label"/></label>
<input type="radio" name="{FieldName}"/>" class="jqueryValidate"/>
</xsl:if>

<xsl:if test="@Required='0'">
<label><xsl:value-of select="@Label"/></label>
<xsl:for-each select="Option">
<input type="radio" name="Field2" Value="1"/> Just One
</xsl:for-each>
</xsl:if>

</xsl:if>
</xsl:for-each>
</fieldset>
</form>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

祝你好运!