文档摘要模型:不同的观点






4.33/5 (2投票s)
一个类库,用于将 XML 类文档的标签结构内化到类中。
引言
本文解释了我开发的一个新类库,用于将类 XML 文档内部化为类。为简洁起见,我没有使用该程序集从文档中提取标签内容。我只是试图展示该程序集确实能一致地工作。届时,我将添加使用此程序集的新 Windows Forms 项目。实际上,此程序集的作用是从文档中提取 HTML 或 XML 标签、属性和文本。您可以在此图像中看到 _seq
的 Count
(938):这是页面的单元格编号(标签编号)。
Using the Code
首先,要使用 DLL,您必须添加引用,然后添加 using ddm.Html
。现在,它已准备就绪。您首先从文档文件中读取文本,并将此 string
传递给构造函数。用于解析 HTML 的主类是 HtmlDDM
。它有两个构造函数。一个接受 string
参数,另一个不接受。您像这样使用主类
StreamReader file = new StreamReader(@"HtmlFiles/page.html", true);
HtmlDDM doc = new HtmlDDM(file.ReadToEnd());
doc.Fill(pbFill);
这里,pbFill
是一个 ProgressBar
对象。HtmlDDM
接收它并运行其 PerformStep()
方法。现在,让我解释一下 Fill()
的作用。它读取所有开标签、闭标签、自闭标签、DOCTYPE
、CDATA
标签,并为每个标签设置变量。然后,它填充主结构以从文档中提取数据:块。我开发了一个嵌套的块结构。有一个 Block
类。这个类有一个 List<Block>
类型的 Childs
属性。正如您所预料的,这种结构类似于 HTML 块结构:标签内有标签,标签周围有标签。标签内有标签发现自身处于嵌套块中。block
对象的 StartCell
和 EndCell
属性在 Fill()
方法中设置;嵌套块可以很容易地通过两个单元格识别:开始和结束。父块中的嵌套块在内部开始和结束。对于标签周围的标签(如自闭、CDATA
、DOCTYPE
等),块也已识别。但这次,startCell
等于 endCell
,并且每个都是自闭单元格类型。我将所有类型的标签都命名为以 "<
" 开头和以 ">
" 结尾。在这里,我们扩展 Cell
类的属性
//in Cell.cs Cell class properties:
public Attributes Attrs { get; set; }
public int x { get; set; }
public int y { get; set; }
public int alpha { get; set; }
public int beta { get; set; }
public int Index { get; set; }
internal int iBgn { get; set; }
internal int iEnd { get; set; }
internal string Name { get; set; }
internal string Type { get; set; }
Attrs
是一个实现 IList<Attribute>
的 Attributes
类对象。该 Attributes
对象是 Attribute
对象的列表。x
、y
、alpha
、beta
对程序的Workflow非常重要。它们是边框标签(开标签和闭标签)的签名者。如果在 FillSequence()
期间,程序计算到一个开标签,它就递增 x
,否则递增 y
。这个流程是向前的。另一种流程是向后的,并改变 alpha
和 beta
。当向后流程运行时,程序递增 alpha
用于开标签,递增 beta
用于闭标签。接下来是 FillBlocks()
。它用 Block
对象填充 rootBlock
,每个对象都有一个 List<Block>
类型的 Childs
列表。因此,一个块可以包含子块(就像一个 HTML 块包含头部和主体块),这些子块也可能包含子块。这就是我提出的文档摘要模型。在此图像中,您可以清楚地看到这一点
现在,这是 CellSequence
类的方法和属性
这里的 FillBackAndForth()
使得 x
、y
、alpha
和 beta
向前和向后流动。在该方法之后,Methods.IsCellBalanced()
获取第一个单元格并检查是否存在不平衡。如果没有,则序列完整。如果有,程序将抛出异常,并显示一条消息,说明不允许标签不足的文档。但是程序如何判断标签结构不足(例如未闭合的 td
标签或错误使用的自闭合方案)呢?这通过以下计算完成:当程序填充 x
、y
、alpha
、beta
时,结构变得有意义;因为,任意单元格的 x
是到该单元格为止的开标签计数,y
是从开头到该单元格为止的闭标签计数。而且,该单元格的 alpha
是从末尾开始的开单元格计数,beta
是从末尾开始的闭标签计数。为了便于理解,我将在一个包含两个单元格的简单文档中给出第一个和最后一个单元格的值
firstCell:Open
lastCell:Close
firstCell: x=1, y=0; alpha=1, beta=1;
lastCell: x=1, y=1;alpha=0, beta=1;
在这里,正如您所看到的,对于开单元格,x-y = beta-alpha + 1,对于闭单元格,x-y = beta-alpha - 1。这使得单元格平衡。如果边框单元格(开和闭)的所有结构都完整,则**任何**单元格的平衡检查都将成功,否则,它永远不会成功。在这里,是时候强调单元格序列由 CellSequence
类表示。正如您所看到的,它实现了 IList<Cell>
。这很重要,因为 Add()
方法必须增加单元格的 Index
属性。顺便说一下,单元格包含一个名为 Index
的属性。此属性适用于所有单元格。但是 x
、y
、alpha
、beta
仅适用于边框单元格。上面,GetCorrespondingCell()
也很重要。它找到起始单元格的结束单元格,以及结束单元格的起始单元格。但是如何找到呢?使用这个计算:正如我之前提到的,边框单元格有四个值(我们称它们为跟踪标志)。通过对跟踪标志进行一些计算,您可以找到相应的标志。如果您有一个方法可以找到单元格的深度,您可以将其他单元格的深度与 firstCell
进行比较,当您找到匹配项时,将其作为 endCell
并停止。让我向您展示如何操作
如您所见,开始单元格和结束单元格的深度相同。但是,我们如何仅从跟踪标记中找到任意单元格的深度呢?很简单:如果是开标签:x-y-1;如果是闭标签:x-y。您可以尝试之前给出的两个单元格文档示例:1-0-1 (firstCell
) == 1-1 (endCell
)。您可以相信它在 w3.org 页面上包含 938 个单元格的文档中也能正常工作。
现在,我将为感兴趣的读者解释一下这个库
Base
命名空间用于基类及其依赖项。基类是 DDM_Base
。它是一个 abstract
类。基类位于 Base.Classes
中。还有一个类:Block
。Enumerations
、Exceptions
和 Methods
类存在于 ToolBox
命名空间中。Methods
是一个 static
类,因为它封装了一些程序中将使用的必要方法,但出于性能原因,无需将其包含在实例中。Exceptions
类包含 Base
的异常。我最喜欢的命名空间是 Settings
命名空间。它包含程序的额外分隔符设置。在未来的版本中,我计划让程序从外部设置文件中获取其设置。因此,例如,如果您想为 HTML 4.0 添加新的分隔符(例如,DELIM),您只需编辑 XML 设置文件,程序启动时它就在那里。我认为是时候谈谈它了。在 Settings
中,有一个 Delimeter.cs 文件
internal class Delimeter
{
public Delimeter()
{
}
internal string Begin { get; set; }
internal string End { get; set; }
internal string Type { get; set; }
}
internal class StandartDelimeter : Delimeter
{
public StandartDelimeter()
{
}
public StandartDelimeter(string begin, string end, string type)
{
Begin = begin;
End = end;
Type = type;
}
}
此文件中有两个类。一个用于额外的分隔符,如 CDATA
、DOCTYPE
等。这些分隔符不是开或闭类型。它们是额外的。而且,每个都采用自己的类型。标准分隔符是 "<
"、">
" 和 "Standart
"。我使程序可扩展,以便您可以更改标准分隔符。在 Delims
static
类中,有一个 static
构造函数,它读取分隔符(标准、边框、额外)并填充 Standart
、Borders[]
和 Extra
变量。然后,在程序中,它们在各种地方使用。
Cells
命名空间是关于 Cell
类及其依赖项的。Attribute
是一个简单的类,具有名称和值属性。Attributes
类是 IList<Attribute>
的实现。CellSequence
是 IList<Cell>
。
关注点
我曾多次尝试寻找一个好的解决方案。一开始,我制作了一个基于文本搜索的文档解析库。但是用它解析一个中等水平的 HTML 文档需要 7 分钟。所以我放弃了并启动了这个项目。
历史
这个版本运行得更好;它还允许用户在 page.html 中搜索指定的属性名称和相应的值。