Создание HTML-формы с использованием XML для определения макета C#

#c# #xml #.net-3.5

#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. Но я надеюсь, что это не так, и вы, профессионалы, можете помочь мне указать правильное направление в этом.

Заранее спасибо 🙂

Дэйв

Комментарии:

1. Я подозреваю, что LINQ для XML или аналогичного позволит найти более краткое решение, если XSLT не подходит.

2. Вы можете использовать XSLT в C # msdn.microsoft.com/en-us/library /…

Ответ №1:

Одним из способов достижения этой цели было бы разработать некоторые объекты, представляющие различные 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
  

Очевидно, что у вас будет Element производный класс для каждого элемента HTML. У нас также есть способ инициализировать их из узла 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-документ в objects. Я предполагаю, что на этом этапе у нас будет какой-то корневой объект. Я собираюсь назвать это 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();
  

БАМ. Мы закончили.

Извините, если в коде есть какие-либо ошибки, я попробую разобраться с этим. Я не запускал это, но я делал что-то подобное раньше. Я также не предполагаю, что это лучший способ, я уверен, что есть и другие. Это один из способов. Я опустил отображение атрибутов по соображениям краткости, но я буду рад добавить еще один конкретный Element пример, чтобы показать, как это может работать.

Ответ №2:

Извините, что подтверждаю ваши опасения, но нет «общего» или «упрощенного» способа сделать это.

Как вы упомянули, XSLT является стандартной практикой, в противном случае вам потребуется создать какой-то пользовательский анализатор / конструктор строк, который делает то же самое.

Один из подходов, который приходит на ум, — это использование MVC-фреймворка для уменьшения рабочей нагрузки.

Фреймворки MVC поставляются с функциями, подобными ‘autobinder’, которые в основном принимают любой класс и генерируют соответствующий HTML (используя какой-либо механизм просмотра).

Итак, если вы выбрали этот маршрут, ваша задача будет ограничена простым синтаксическим анализом XML и созданием хорошо сформированных классов с аннотациями. Затем платформа MVC сможет генерировать для вас все CRUD-представления в HTML.

Надеюсь, это поможет, приветствия.

Ответ №3:

Я не знаю, насколько вы против использования XSLT, но вы можете добиться этого, используя XSLT в C # следующим образом.

 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>
  

Удачи!