#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>
Удачи!