第四章 IRIS 使用%XML.TextReader

%XML.TextReader提供了一种简单易行的方法来读取任意XML文档,这些文档可能直接映射到InterSystems IRIS对象,也可能不直接映射到。具体来说,这个类提供了导航格式良好的XML文档并查看其中的信息(元素、属性、注释、命名空间URI等)的方法。这个类还提供了基于DTD或XML模式的完整文档验证。但是,与%XML.Reader不同,%XML.TextReader不提供返回DOM的方法。如果您需要DOM,请参阅将XML导入对象。

注:

您使用的任何XML文档的XML声明都应该指示该文档的字符编码,并且该文档应该按照声明的方式进行编码。如果未声明字符编码,InterSystems IRIS将使用输入和输出的字符编码中描述的默认值。如果这些默认值不正确,请修改XML声明,使其指定实际使用的字符集。

4.1 创建Text Reader方法

要读取不一定与InterSystems IRIS对象类有任何关系的任意XML文档,请调用%XML.TextReader的方法,该方法将打开文档并将其作为文本读取器对象加载到临时存储中。文本读取器对象包含一个可导航的节点树,每个节点都包含有关源文档的信息。然后,您的方法可以导航文档并找到有关它的信息。对象的属性为您提供有关文档的信息,这些信息取决于您在文档中的当前位置。如果存在验证错误,那么这些错误也可以作为树中的节点使用。

4.1.1 总体结构

您的方法应该执行以下部分或全部操作:

  1. 通过以下方法之一的第一个参数指定文档源:
方法第一个自变量
ParseFile()具有完整路径的文件名。请注意,文件名和路径必须仅包含ASCII字符。
ParseStream()A stream
ParseString()A string
ParseURL()A URL

在任何情况下,源文档都必须是格式良好的XML文档;也就是说,它必须遵守XML语法的基本规则。这些方法中的每一个都返回一个状态($$$OK或失败代码),以指示结果是否成功。您可以使用通常的机制来测试状态;特别是,您可以使用$System.Status.DisplayError(Status)来查看错误消息的文本。

对于这些方法中的每一个,如果该方法返回$$$OK,它将通过引用(其第二个参数)返回包含XML文档中信息的文本读取器对象。

通过其他参数可以控制实体解析、验证、找到哪些项等。有关分析方法,请参阅参数列表。

  1. 检查解析方法返回的状态,如果合适,请退出。

    如果解析方法返回$$$OK,那么您有一个与源XML文档相对应的文本读取器对象。您可以导航此对象。

    您的文档可能包含“element”、“endelement”、“startprefixmapping”等节点。节点类型列在“节点类型”中。

重要:

在出现任何验证错误的情况下,您的文档包含“错误”或“警告”节点。您的代码应该检查这些节点。请参阅执行验证。

  1. 使用以下实例方法之一开始读取文档。

    • 使用Read()导航到文档的第一个节点。

    • 使用ReadStartElement()导航到特定类型的第一个元素。

    • 使用MoveToContent()导航到第一个类型为“chars”的节点。

      请参见导航文档。

  2. 获取此节点感兴趣的属性的值(如果有)。可用的属性包括名称、值、深度等。请参见节点属性。

  3. 根据需要继续浏览文档并获取属性值。

    如果当前节点是一个元素,则可以使用MoveToAttributeIndex()MoveToAttributeName()方法将焦点移动到该元素的属性。若要返回到元素(如果适用),请使用MoveToElement()

  4. 如果需要,请使用Rewind()方法返回到文档的开头(在第一个节点之前)。这是唯一一个可以在源代码中返回的方法。

方法运行后,文本读取器对象将被销毁,并且所有相关的临时存储都将被清理。

4.1.2 示例1

这里有一个简单的方法,可以读取任何XML文件,并显示每个节点的序列号、类型、名称和值:

ClassMethod WriteNodes(myfile As %String)
{
    set status=##class(%XML.TextReader).ParseFile(myfile,.textreader)
    //check status
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit}
    //iterate through document, node by node
    while textreader.Read()
    {
        Write !, "Node ", textreader.seq, " is a(n) "
        Write textreader.NodeType," "
        If textreader.Name'=""
        {
            Write "named: ", textreader.Name
            }
            Else
            {
                Write "and has no name"
                }
        Write !, "    path: ",textreader.Path
        If textreader.Value'="" 
        {
            Write !, "    value: ", textreader.Value
            }
        }
}

此示例执行以下操作:

  1. 它调用ParseFile()类方法。这将读取源文件,创建一个文本读取器对象,并通过引用将其返回到变量doc中。
  2. 如果ParseFile()成功,该方法将调用Read()方法来查找文档中的每个连续节点。
  3. 对于每个节点,该方法都会写入输出行,其中包含节点的序列号、节点类型、节点名称(如果有)、节点路径和节点值(如果有的话)。输出被写入当前设备。

以下示例源文档:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="mystyles.css"?>
<Root>
   <s01:Person xmlns:s01="http://www.root.org">
      <Name attr="xyz">Willeke,Clint B.</Name>
      <DOB>1925-10-01</DOB>
   </s01:Person>
</Root>

对于此源文档,前面的方法生成以下输出:

Node 1 is a(n) processinginstruction named: xml-stylesheet
    path:
    value: type="text/css" href="mystyles.css"
Node 2 is a(n) element named: Root
    path: /Root
Node 3 is a(n) startprefixmapping named: s01
    path: /Root
    value: s01 http://www.root.org
Node 4 is a(n) element named: s01:Person
    path: /Root/s01:Person
Node 5 is a(n) element named: Name
    path: /Root/s01:Person/Name
Node 6 is a(n) chars and has no name
    path: /Root/s01:Person/Name
    value: Willeke,Clint B.
Node 7 is a(n) endelement named: Name
    path: /Root/s01:Person/Name
Node 8 is a(n) element named: DOB
    path: /Root/s01:Person/DOB
Node 9 is a(n) chars and has no name
    path: /Root/s01:Person/DOB
    value: 1925-10-01
Node 10 is a(n) endelement named: DOB
    path: /Root/s01:Person/DOB
Node 11 is a(n) endelement named: s01:Person
    path: /Root/s01:Person
Node 12 is a(n) endprefixmapping named: s01
    path: /Root
    value: s01
Node 13 is a(n) endelement named: Root
    path: /Root

请注意,注释已被忽略;默认情况下,%XML.TextReader会忽略注释。有关更改此项的信息,请参见分析方法的参数列表。

4.1.3 示例2

以下示例读取一个XML文件并列出其中的每个元素:

ClassMethod ShowElements(myfile As %String)
{
    set status = ##class(%XML.TextReader).ParseFile(myfile,.textreader)
    //check status
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit}
    //iterate through document, node by node
    while textreader.Read()
    {
        if (textreader.NodeType = "element") 
        {
            write textreader.Name,!
            }
        }
}

此方法通过使用NodeType属性来检查每个节点的类型。如果节点是一个元素,则该方法会将其名称打印到当前设备。对于前面显示的XML源文档,此方法生成以下输出:

Root
s01:Person
Name
DOB

4.2 节点类型

文档的每个节点都是以下类型之一:

文本读取器文档中的节点类型

类型描述
“attribute”XML属性。
“chars”一组字符(例如元素的内容)。%XML.TextReader可以识别其他节点类型(“CDATA”、“EntityReference”和“EndEntity”),但会自动将它们转换为“chars”。
“comment”XML注释。
“element”XML元素的开头。
“endelement”XML元素的结尾。
“endprefixmapping”声明命名空间的上下文的末尾。
“entity”一个XML实体。
“error”解析程序发现验证错误。请参阅执行验证。
“ignorablewhitespace”混合内容模型中标记之间的空白。
“processinginstruction”一种XML处理指令。
“startprefixmapping”XML命名空间声明,该声明可以包含也可以不包含命名空间。
“warning”解析程序发现一个验证警告。请参阅执行验证。

请注意,一个XML元素由多个节点组成。例如,考虑以下XML片段:

<Person>
   <Name>Willeke,Clint B.</Name>
   <DOB>1925-10-01</DOB>
</Person>

SAX解析器将此XML视为以下一组节点:

文档节点示例

节点编号节点类型节点名称(如果有)节点的值(如果有
1elementPerson
2elementName
3charsWilleke,Clint B.
4endelementName
5elementDOB
6chars1925-10-01
7endelementDOB
8endelementPerson

例如,请注意,<DOB>元素被认为是三个节点:一个元素节点、一个字符节点和一个结束节点。还要注意,这个元素的内容只能作为chars节点的值使用。

4.3 节点属性

%XML.TextReader解析XML文档并创建一个文本读取器对象,该对象由一组与文档组件相对应的节点组成;节点类型在文档节点中进行了描述。

将焦点更改为其他节点时,文本读取器对象的属性将更新,以包含有关当前正在检查的节点的信息。本节介绍%XML.TextReader的所有属性。

AttributeCount

如果当前节点是元素或属性,则此属性指示元素的属性数。在给定的元素中,第一个属性编号为1。

对于任何其他类型的节点,此属性为0。

Depth

指示文档中当前节点的深度。根部元素位于深度1处;根元素之外的项位于深度0处。请注意,属性的深度与其所属元素的深度相同。类似地,错误或警告与导致错误或警告的项目处于相同的深度。

HasAttributes

如果当前节点是一个元素,则如果该元素具有属性,则此属性为true(如果没有属性,则为false)。如果当前节点是一个属性,则此属性为true。

对于任何其他类型的节点,此属性都是false。

HasValue

如果当前节点是具有值的节点类型(即使该值为null),则为True。否则,此属性为false。具体而言,此属性适用于以下类型的节点:

  • attribute
  • chars
  • comment
  • entity
  • ignorablewhitespace
  • processinginstruction
  • startprefixmapping

IsEmptyElement

如果当前节点是一个元素并且为空,则为True。否则,此属性为false。

LocalName

对于attribute、element或endelement类型的节点,这是当前元素或属性的名称,没有命名空间前缀。对于所有其他类型的节点,此属性为null。

Name

当前节点的完全限定名称,视节点类型而定。下表给出了详细信息:

节点名称(按类型)

节点类型名称和示例
attribute属性的名称。例如,如果属性为:groupID="GX078" 则名称为:groupID
elementorendelement元素的名称。例如,如果一个元素是:<s01:Person groupID="GX078">...</s01:Person> 则名称为:s01:Person
entity实体的名称。
startprefixmappingorendprefixmapping前缀(如果有)。例如,如果命名空间声明如下:xmlns="http://www.root.org" 则名称为null
processinginstruction处理指令的目标。例如,如果处理指令是:<?xml-stylesheet type="text/css" href="mystyles.css"?>则名称为:xml-stylesheet
all other typesnull

NamespaceUri

对于attribute、element或endelement类型的节点,这是属性或元素所属的命名空间(如果有的话)。对于所有其他类型的节点,此属性为null。

NodeType

当前节点的类型。请参见文档节点。

Path

元素的路径。例如,考虑以下XML文档:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="mystyles.css"?>
<s01:Root xmlns:s01="http://www.root.org" xmlns="www.default.org">
   <Person>
      <Name>Willeke,Clint B.</Name>
      <DOB>1925-10-01</DOB>
      <GroupID>U3577</GroupID>
      <Address xmlns="www.address.org">
         <City>Newton</City>
         <Zip>56762</Zip>
      </Address>
   </Person>
</s01:Root>

对于 City 元素,Path 属性为/s01:Root/Person/Address/City。对其他元素进行类似处理。

ReadState

指示文本读取器对象的总体状态,该对象是以下对象之一:

  • “Initial”表示尚未调用Read()方法。

  • “Interactive”表示Read()方法至少被调用过一次。

  • “EndOfFile”表示已到达文件的末尾。

Value

当前节点的值(如果有),视节点类型而定。下表给出了详细信息:

按类型列出的节点值

Node TypeValue and Example
attribute属性的值。例如,如果属性为:groupID=“GX078”,则值为:GX078
chars文本节点的内容。例如,如果一个元素是:1925-10-01,那么对于chars节点,Value是:1925-10-01
comment评论的内容。例如,如果注释为:***,则Value为:comment here
entity实体的定义。
error错误消息。有关示例,请参见执行验证。
ignorablewhitespace空白处的内容。
processinginstruction处理指令的全部内容,不包括目标。例如,如果处理指令是:<?xml样式表type=“text/css”href=“mystyles.css”?>则值为:type=“text/css”href=“mystyles.css”?
startprefixmapping前缀,后面跟着一个空格,后面跟着URI。例如,如果命名空间声明如下:xmlns:s01=“http://www.root.org则值为:s01http://www.root.org
warning警告信息。有关示例,请参见执行验证。
all other types (including element)null

seq

文档中此节点的序列号。第一个节点编号为1。请注意,属性与它所属的元素具有相同的序列号。

4.4 解析方法的参数列表

要指定文档源,请使用文本读取器的ParseFile()ParseStream()PareString()ParseURL()方法。在任何情况下,源文档都必须是格式良好的XML文档;也就是说,它必须遵守XML语法的基本规则。对于这些方法,只需要前两个参数。为了参考,这些方法按顺序具有以下参数:

  1. Filename, Stream, String, 或者 URL — 文档源。

    请注意,对于ParseFile()Filename参数必须仅包含ASCII字符。

  2. TextReader — 文本读取器对象,如果方法返回$$$OK,则作为输出参数返回。

  3. Resolver — 解析源时要使用的实体解析程序。请参阅自定义SAX解析器的使用方式中的执行自定义实体解析。

  4. Flags — 用于控制SAX解析器执行的验证和处理的标志或标志组合。请参阅自定义SAX解析器的使用方式中的设置解析器标志。

  5. Mask — 一个掩码,用于指定XML源中感兴趣的项。请参阅自定义SAX解析器的使用方式中的指定事件掩码。

    提示:

    对于%XML.TextReader的解析方法,默认掩码为$$$SAXCONTENTEVENTS。请注意,这会忽略注释。要解析所有可能类型的节点,请使用$$$SAXALLEVENTS作为此参数。请注意,这些宏是在%occSAX.inc包含文件中定义的。

  6. SchemaSpec — 一种模式规范,用于验证文档源。此参数是一个字符串,包含以逗号分隔的命名空间/URL对列表:

    "namespace URL,namespace URL"
    
  7. KeepWhiteSpace — 是否保留空白的选项。

  8. pHttpRequest — (仅适用于ParseURL()方法)对web服务器的请求,作为%Net.HttpRequest的实例。默认情况下,系统创建一个%Net.HHttpRequest的新实例并使用该实例,但您可以使用%Net.HTTPRequest的其他实例发出请求。如果有一个预先存在的%Net.HttpRequest已设置代理和其他属性,这将非常有用。此选项仅适用于http类型的URL(例如,不适用于文件或ftp)。

    有关%Net.HttpRequest的详细信息,请参阅使用Internet实用程序。或者查看%Net.HttpRequest的类文档。

4.5 浏览文档

要浏览文档,请使用文本读取器的以下方法:Read(), ReadStartElement(), MoveToAttributeIndex(), MoveToAttributeName(), MoveToElement(), MoveToContent(), 和Rewind().

4.5.1 导航到下一个节点

要移动到文档中的下一个节点,请使用Read()方法。Read()方法返回一个真值,直到没有更多的节点可以读取为止(也就是说,直到到达文档的末尾)。前面的例子在循环中使用了这种方法,如下所示:

 While (textreader.Read()) {

...

 }

4.5.2 导航到特定元素的首次出现

您可以移动到文档中第一个出现特定元素的位置。为此,请使用ReadStartElement()方法。除非找不到元素,否则此方法将返回true。如果找不到元素,则该方法将到达文件的末尾。

ReadStartElement()方法接受两个参数:元素的名称和(可选)命名空间URI。请注意,%XML.TextReader不会对命名空间前缀进行任何处理。因此,ReadStartElement()方法将以下两个元素视为具有不同的名称:

<Person>Smith,Ellen W. xmlns="http://www.person.org"</Person>

<s01:Person>Smith,Ellen W. xmlns:s01="http://www.person.org"</s01:Person>

4.5.3 导航到属性

导航到某个元素时,如果该元素具有属性,则可以通过以下两种方式之一导航到它们:

  • 使用MoveToAttributeIndex()方法按索引(属性在元素中的顺序位置)移动到特定属性。此方法采用一个参数:属性的索引号。您可以使用AttributeCount属性来了解给定元素具有多少属性;有关所有属性的列表,请参见节点属性。
  • 使用MoveToAttributeName()方法按名称移动到特定的属性。此方法接受两个参数:属性的名称和(可选)命名空间URI。请注意,%XML.TextReader不会对命名空间前缀进行任何处理;如果属性有前缀,则该前缀被视为属性名称的一部分。

完成当前元素的属性后,可以通过调用其中一个导航方法(如Read())来移动到文档中的下一个元素。或者,您可以调用MoveToElement()方法返回到包含当前属性的元素。

例如,以下代码按索引号列出了当前节点的所有属性:

 If (textreader.NodeType = "element") {
     // list attributes for this node
     For a = 1:1:textreader.AttributeCount {
         Do textreader.MoveToAttributeIndex(a)
         Write textreader.LocalName," = ",textreader.Value,!
     }
 }

以下代码查找当前节点的颜色属性的值:

 If (textreader.NodeType = "element") {
     // find color attribute for this node
     If (textreader.MoveToAttributeName("color")) {
         Write "color = ",textreader.Value,!
     }
 }

4.5.4 导航到包含内容的下一个节点

MoveToContent()方法可以帮助您查找内容。明确地:

  • 如果节点是“chars”以外的任何类型,则此方法前进到“chars“类型的下一个节点。
  • 如果节点的类型为“chars”,则此方法不会在文件中前进。

4.5.5 重返

这里描述的所有方法都在文档中前进,除了Rewind()方法,它导航到文档的开头并重置所有属性。

4.6 执行验证

默认情况下,根据提供的任何DTD或模式文档来验证源文档。如果文档中包含一个DTD部分,则该文档将根据该DTD进行验证。要根据架构文档进行验证,请在ParseFile()ParseStream()PareString()ParseURL()的参数列表中指定架构,如Parse方法的参数列表所述。

大多数类型的验证问题都是非致命的,会导致错误或警告。具体地说,“错误”或“警告”类型的节点会自动添加到文档树中错误发生的位置。您可以使用与任何其他类型的节点相同的方式导航到这些节点并对其进行检查。

例如,考虑以下XML文档:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Root [
  <!ELEMENT Root (Person)>
  <!ELEMENT Person (#PCDATA)>
]>
<Root>
   <Person>Smith,Joe C.</Person>
</Root>

在这种情况下,我们预计不会出现任何验证错误。回想一下本主题前面显示的示例方法WriteNodes()。如果我们使用这种方法来阅读本文档,输出将如下所示:

Node 1 is a(n) element named: Root
    and has no value
Node 2 is a(n) ignorablewhitespace and has no name
    with value:
 
Node 3 is a(n) element named: Person
    and has no value
Node 4 is a(n) chars and has no name
    with value: Smith,Joe C.
Node 5 is a(n) endelement named: Person
    and has no value
Node 6 is a(n) ignorablewhitespace and has no name
    with value:
 
Node 7 is a(n) endelement named: Root
    and has no value

相反,假设文件看起来是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Root [
  <!ELEMENT Root (Person)>
  <!ELEMENT Person (#PCDATA)>
]>
<Root>
   <Employee>Smith,Joe C.</Employee>
</Root>

在这种情况下,我们预计会出现错误,因为<Employee>元素没有在DTD部分中声明。在这里,如果我们使用示例方法WriteNodes()来读取此文档,则输出如下:

Node 1 is a(n) element named: Root
    and has no value
Node 2 is a(n) ignorablewhitespace and has no name
    with value:
 
Node 3 is a(n) error and has no name
    with value: Unknown element 'Employee' 
while processing c:/TextReader/docwdtd2.txt at line 7 offset 14
Node 4 is a(n) element named: Employee
    and has no value
Node 5 is a(n) chars and has no name
    with value: Smith,Joe C.
Node 6 is a(n) endelement named: Employee
    and has no value
Node 7 is a(n) ignorablewhitespace and has no name
    with value:
 
Node 8 is a(n) error and has no name
    with value: Element 'Employee' is not valid for content model: '(Person)' 
while processing c:/TextReader/docwdtd2.txt at line 8 offset 8
Node 9 is a(n) endelement named: Root
    and has no value

另请参阅自定义SAX解析器的使用方式中的设置解析器标志。

4.7 示例:命名空间报告

以下示例方法读取任意XML文件,并指示每个元素和属性所属的命名空间:

ClassMethod ShowNamespacesInFile(filename As %String)
{
  Set status = ##class(%XML.TextReader).ParseFile(filename,.textreader)
  
  //check status
  If $$$ISERR(status) {do $System.Status.DisplayError(status) quit}
  
  //iterate through document, node by node
  While textreader.Read()
  {
    If (textreader.NodeType = "element")
    {
       Write !,"The element ",textreader.LocalName
       Write " is in the namespace ",textreader.NamespaceUri
       }
    If (textreader.NodeType = "attribute")
    {
       Write !,"The attribute ",textreader.LocalName
       Write " is in the namespace ",textreader.NamespaceUri
       }
     }
}

当在终端中使用时,此方法会产生如下输出:

 
The element Person is in the namespace www://www.person.com
The element Name is in the namespace www://www.person.com

以下变体接受启用XML的对象,将其写入流,然后使用该流生成相同类型的报告:

ClassMethod ShowNamespacesInObject(obj)
{
  set writer=##class(%XML.Writer).%New()

  set str=##class(%GlobalCharacterStream).%New()
  set status=writer.OutputToStream(str)
  if $$$ISERR(status) {do $System.Status.DisplayError(status) quit ""}

  //write to the stream
  set status=writer.RootObject(obj)
  if $$$ISERR(status) {do $System.Status.DisplayError(status) quit }

  Set status = ##class(%XML.TextReader).ParseStream(str,.textreader)
  
  //check status
  If $$$ISERR(status) {do $System.Status.DisplayError(status) quit}
  
  //iterate through document, node by node
  While textreader.Read()
  {
    If (textreader.NodeType = "element")
    {
       Write !,"The element ",textreader.LocalName
       Write " is in the namespace ",textreader.NamespaceUri
       }
    If (textreader.NodeType = "attribute")
    {
       Write !,"The attribute ",textreader.LocalName
       Write " is in the namespace ",textreader.NamespaceUri
       }
     }
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值