对XML+XSLT的转换应用特定的过程以改变生成的HTML代码

  在使用 XslCompiledTransform 对 XML 应用 XSLT 样式表时,转换的结果是写入 XmlWriter,而在平时应用时,可能会遇到对特定的非 HTML 标签转换为 HTML 标签以及附加 javascript 脚本的情况,而且无法直接通过 XSLT 来实现转换;此外,使用 XmlTextWriter 进行写入时对于 <div></div> 这样的标签会被写入为 <div /> 导致浏览器可能不能正常解析,而且因为要考虑 XML 的情况,写入效率较低。因此我们在这里从 XmlWriter 派生一个类,来实现我们需要的功能。

  我们将这个类叫做 XhtmlWriter,实现的原理很简单,在调用类的写入方法时,并不将数据写入基础 TextWriter,而是将数据写入树状的数据结构中,在调用 Flush 方法时,对树状数据结构的每个结点应用处理过程,然后将每个结点按照 XHTML 格式写入基础 TextWriter。

  首先定义树状数据结构的结点类——XmlElementEntity:

public sealed class XmlElementEntity
{
    
public bool Content { getset; }
    
// 结点是否作为文本内容而非子结点
    public string Element { getset; }
    
// 结点名(HTML 标签名)或者是文本内容
    public Dictionary<stringstring> Attributes { getset; }
    
// 属性集合
    public XmlElementEntity Parent { getset; }
    
// 父结点
    public List<XmlElementEntity> Children { getset; }
    
// 子结点集合
    public XmlElementEntity()
    {
        Content 
= false;
        Attributes 
= new Dictionary<stringstring>();
        Children 
= new List<XmlElementEntity>();
    }
}
  接下来是结点处理过程的接口定义——IXhtmlExtendWriter:
public interface IXhtmlExtendWriter
{
    
void Flush(XhtmlWriter writer, XmlElementEntity entity);
}
  最后就是 XhtmlWriter 类了,它继承 XmlWriter,对于 XmlWriter 的大多数抽象方法我们都是用不到的,我们用到的只有 Close、Flush、WriteEndAttribute、WriteEndElement、WriteStartAttribute、WriteStartElement、WriteString 以及 get_WriteState,其他的直接 throw new NotSupportedException() 就可以了。其中的属性和变量:
    public TextWriter ScriptWriter { getprivate set; }
    
// 用于写入 javascript 的 TextWriter
    public XmlElementEntity RootEntity { getprivate set; }
    
// 根结点
    public XmlElementEntity CurrentEntity { getprivate set; }
    
// 当前正在写入的结点
    public override WriteState WriteState
    {
        
get
        {
            
return writeState;
        }
    }
    
// 写入状态属性
    private TextWriter writer;
    
// 用于写入 HTML 的基础 TextWriter
    private WriteState writeState;
    
// 写入状态
    private IXhtmlExtendWriter[] extends;
    
// 结点处理过程集合
    private string attribute;
    
// 当前整写入的属性名

  接下来是构造函数和已实现的抽象方法:

    public XhtmlWriter(TextWriter scriptWriter, TextWriter writer)
    {
        ScriptWriter 
= scriptWriter;
        
this.writer = writer;
        extends 
= new IXhtmlExtendWriter[] { new NavigateExtendWriter(),
            
new ValidateCodeExtendWriter(),
            
new ValidationExtendWriter() };
        
// 结点处理过程,也可以应用工厂模式来创建
        CurrentEntity = RootEntity = new XmlElementEntity();
        
// 初始化根结点,并将当前结点设置为根结点
    }

    
public override void Close()
    {
        CurrentEntity 
= RootEntity = null;
        ScriptWriter.Close();
        writer.Close();
        
// 结束写入过程,关闭所有 TextWriter
    }

    
public override void Flush()
    {
        
foreach (XmlElementEntity child in RootEntity.Children)
            Flush(child);
        
// 遍历根结点的子结点
        ScriptWriter.Flush();
        writer.Flush();
        
// 写入所有 TextWriter 缓冲
        CurrentEntity = RootEntity = new XmlElementEntity();
    }

    
private void Flush(XmlElementEntity entity)
    {
        
// 写入每个结点
        foreach (IXhtmlExtendWriter extend in extends)
            extend.Flush(
this, entity);
        
// 对每个结点应用每个处理过程
        if (entity.Content)
        {
            writer.Write(entity.Element);
            
return;
        }
        
// 如果为文本内容则直接写入文本并退出
        writer.Write('<');
        writer.Write(entity.Element);
        
// 写入起始标签
        foreach (KeyValuePair<stringstring> kv in entity.Attributes)
        {
            writer.Write(
' ');
            writer.Write(kv.Key);
            writer.Write(
"=\"");
            writer.Write(kv.Value);
            writer.Write(
"\"");
        }
        
// 遍历写入所有属性
        switch (entity.Element.ToLower())
        {
            
case "input":
            
case "img":
            
case "br":
                writer.Write(
"/>");
                
return;
        }
        
// 根据标签名写入不同的结束标记
        writer.Write(">");
        
foreach (XmlElementEntity child in entity.Children)
            Flush(child);
        
// 写入子结点
        writer.Write("</");
        writer.Write(entity.Element);
        writer.Write(
">");
        
// 写入结束标签
    }

    
public override void WriteEndAttribute()
    {
        writeState 
= WriteState.Element;
        
// 更改状态为元素
    }

    
public override void WriteStartAttribute(string prefix, string localName, string ns)
    {
        writeState 
= WriteState.Attribute;
        
// 更改状态为属性
        CurrentEntity.Attributes[attribute = localName] = "";
        
// 添加属性,并保存属性名
    }

    
public override void WriteStartElement(string prefix, string localName, string ns)
    {
        writeState 
= WriteState.Element;
        
// 更改状态为元素
        XmlElementEntity entity = new XmlElementEntity() {
            Parent 
= CurrentEntity,
            Element 
= localName };
        CurrentEntity.Children.Add(entity);
        CurrentEntity 
= entity;
        
// 添加子结点并将当前结点设置为子结点
    }

    
public override void WriteString(string text)
    {
        
if (writeState == WriteState.Attribute)
            CurrentEntity.Attributes[attribute] 
= text;
        
// 如果状态为属性则设置当前属性值
        else
            CurrentEntity.Children.Add(
new XmlElementEntity() {
                Content 
= true,
                Element 
= text,
                Parent 
= CurrentEntity });
        
// 元素状态添加文本内容子结点
        writeState = WriteState.Content;
        
// 更改状态为内容
    }
  在结点处理过程中,可以根据标签名、属性名设置新的标签名和属性,并写入脚本。
posted @ 2008-08-21 15:00  田嵩  阅读(1834)  评论(4编辑  收藏  举报