第五章 IRIS 将XML导入对象

本文详细介绍了如何使用%XML.Reader类在InterSystemsIRIS中导入XML文档,创建与XML元素对应的对象实例,包括设置相关性、处理错误、检查必需元素、忽略空值等,并提供了不同场景的示例代码。
摘要由CSDN通过智能技术生成

%XML.Reader类使您能够将XML文档导入InterSystems IRIS对象。

注:

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

您也可以使用%XML.Reader读取任意XML文档并返回DOM(文档对象模型);请参阅将XML文档表示为DOM。

5.1 概述

InterSystems IRIS提供了用于读取XML文档并创建与该文档的元素相对应的支持XML的InterSystem IRIS对象的一个或多个实例的工具。基本要求如下:

  • 该对象的类定义必须扩展%XML.Adaptor。除了少数例外,该对象引用的类也必须扩展%XML.Adaptor。请参阅将对象投影到XML。

提示:

如果相应的XML模式可用,则可以使用它来生成类(以及任何支持类)。请参阅从XML架构生成类。

  • 若要导入XML文档,请创建一个%XML.Reader实例,然后调用该实例的方法。这些方法指定XML源文档,将XML元素与启用XML的类相关联,并将元素从源读取到对象中。

%XML.Reader与由%XML.Adaptor提供的方法一起工作以执行以下操作:

  • 它使用InterSystems IRIS SAX接口解析和验证传入的XML文档。验证可以包括DTD或XMLSchema验证。
  • 它确定是否有任何启用XML的对象与XML文档中包含的元素相关联,并在读取文档时在内存中创建这些对象的实例。

请注意,由%XML.Reader创建的对象实例不会存储在数据库中;它们在内存对象中。如果要将对象存储在数据库中,则必须调用%Save()方法(对于持久对象)或将相关属性值复制到持久对象并保存。应用程序还必须决定何时插入新数据以及何时更新现有数据;%XML.Reader无法进行这种区分。

下面的终端会话显示了一个简单的示例。在这里,我们将XML文件读取到一个新对象中,检查该对象,然后保存该对象:

GXML>Set reader = ##class(%XML.Reader).%New()
 
GXML>Set file="c:\sample-input.xml"
 
GXML>Set status = reader.OpenFile(file)
 
GXML>Write status
1
GXML>Do reader.Correlate("Person","GXML.Person")
 
GXML>Do reader.Next(.object,.status)
 
GXML>Write status
1
GXML>Write object.Name
Worthington,Jeff R.
GXML>Write object.Doctors.Count()
2
GXML>Do object.%Save()

此示例使用以下示例XML文件:

<Person GroupID="90455">
  <Name>Worthington,Jeff R.</Name>
  <DOB>1976-11-03</DOB>
  <Address>
    <City>Elm City</City>
    <Zip>27820</Zip>
  </Address>
  <Doctors>
    <Doctor>
      <Name>Best,Nora A.</Name>
    </Doctor>
    <Doctor>
      <Name>Weaver,Dennis T.</Name>
    </Doctor>
  </Doctors>
</Person>

5.2 创建导入方法

5.2.1 总体方法结构

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

  1. 创建%XML.Reader的实例。

  2. (可选)指定此实例的“格式”属性,以指定要导入的文件的格式;请参考 Reader 属性。

    默认情况下,InterSystems IRIS假设XML文件是文字格式的。如果您的文件是SOAP编码的格式,则必须指出这一点,以便能够正确读取文件。

  3. (可选)设置此实例的其他属性;请参考 Reader 属性。

  4. 打开源数据。要执行此操作,请使用以下%XML.Reader方法之一:

    • OpenFile() — 打开一个文件。

    • OpenStream() — 打开流。

    • OpenString() — 打开一个字符串。

    • OpenURL() — 打开一个URL。

      如果URL使用HTTPS,请参阅访问HTTPS URL下的文档。

    在每种情况下,您都可以选择为该方法指定第二个参数,以覆盖Format属性的值。

  5. 将此文件中的一个或多个XML元素名称与具有相应结构的支持InterSystems IRIS XML的类关联起来。有两种方法可以做到这一点:

    • 使用Correlate()方法,该方法具有以下签名:
    method Correlate(element As %String, 
                     class As %String, 
                     namespace As %String)
    

    其中,element是XML元素名称,class是InterSystems IRIS类名(带包),namespace是可选的命名空间URI。

    如果使用名称空间参数,则匹配仅限于指定名称空间中的指定元素名称。如果将命名空间参数指定为“”,则与Next()方法中给定的默认命名空间相匹配。如果不使用名称空间参数,则只有元素名称用于匹配。

    提示:

    您可以重复调用Correlate()方法来关联多个元素,尽管本主题中的示例只显示了一个关联。

    • 使用CorrelateRoot()方法,该方法具有以下签名:
    method CorrelateRoot(class As %String)
    
  6. 实例化类实例,如下所示:

    • 如果使用Correlate(),则循环遍历文件中的相关元素,每次一个元素。在循环中,使用Next()方法,该方法具有以下签名:
    method Next(ByRef oref As %ObjectHandle, 
                ByRef sc As %Status, 
                namespace As %String = "") as %Integer
    

    其中oref是该方法创建的对象,sc是状态,namespace是文件的默认名称空间。

    • 如果使用CorrelateRoot(),请调用Next()方法一次,这将导致相关类被实例化。

    Next()方法在到达文件末尾时返回0。如果在此之后再次调用Next(),则将从文件顶部开始再次循环遍历文件中的对象。(您指定的相关性仍然有效。)

5.2.2 错误检查

上一节中提到的大多数方法都返回一个状态。您应该在每个步骤后检查状态,并在适当的情况下退出。

5.2.3 基本导入示例

考虑以下名为test.XML的XML文件:

<Root>
   <Person>
      <Name>Elvis Presley</Name>
   </Person>
   <Person>
      <Name>Johnny Carson</Name>
   </Person>
</Root>

我们首先定义一个支持XML的类MyApp.Person,它是一个人的对象表示:

Class MyApp.Person Extends (%Persistent,%XML.Adaptor)
{
Parameter XMLNAME = "Person";

Property Name As %String;
}

要将此文件导入MyAppPerson类的实例,我们可以编写以下方法:

ClassMethod Import()
{
    // Create an instance of %XML.Reader
    Set reader = ##class(%XML.Reader).%New()

    // Begin processing of the file
    Set status = reader.OpenFile("c:\test.xml")
    If $$$ISERR(status) {do $System.Status.DisplayError(status)}

    // Associate a class name with the XML element name
    Do reader.Correlate("Person","MyApp.Person")
    
    // Read objects from xml file
    While (reader.Next(.object,.status)) {
        Write object.Name,!
    }
    
    // If error found during processing, show it
    If $$$ISERR(status) {do $System.Status.DisplayError(status)}

}

此方法执行以下几个任务:

  • 它使用InterSystems IRIS SAX接口解析输入文件。这包括根据DTD或模式验证文档(如果指定的话)。
  • Correlate()方法将类MyApp.MyPerson与XML元素<Person>相关联;<Person>中的每个子元素都成为MyPerson的属性。
  • 它从输入文件中读取每个<Person>元素,直到没有剩余元素为止。
  • 最后,如果循环因错误而终止,则错误将显示在当前输出设备上。

如上所述,此示例不将对象存储到数据库中。因为MyPerson是一个持久对象,所以可以通过在While循环中添加以下行来实现这一点:

    Set savestatus = object.%Save()
    If $$$ISERR(savestatus) {do $System.Status.DisplayError(savestatus)}

5.2.4 以HTTPS URL访问文档

对于OpenURL()方法,如果文档所在的URL需要SSL/TLS,请执行以下操作:

  1. 使用管理门户创建SSL/TLS配置,该配置包含所需连接的详细信息。有关信息,请参阅InterSystems TLS指南。

    这是一次性的步骤。

  2. 使用%XML.reader时,请设置reader实例的SSLConfiguration属性。对于该值,请指定在上一步中创建的SSL/TLS配置的名称。

或者,当您使用%XML.Reader时,也可以执行以下操作:

  1. 创建%Net.HttpRequest的实例。
  2. 将该实例的SSLConfiguration属性设置为等于您在管理门户中创建的SSL/TLS配置的配置名称。
  3. 使用%Net.HttpRequest的实例作为OpenURL()的第三个参数。

例如:

 set reader=##class(%XML.Reader).%New() 
 set httprequest=##class(%Net.HttpRequest).%New() 
 set httprequest.SSLConfiguration="mysslconfigname" 
 set status=reader.OpenURL("https://myurl",,httprequest) 

当服务器需要身份验证时访问文档

如果服务器需要身份验证,请创建%Net.HttpRequest的实例,并设置该实例的用户名和密码属性。也可以如上所述使用SSL(因此也可以设置SSLConfiguration属性)。然后使用%Net.HttpRequest实例作为OpenURL()的第三个参数,如上面的示例所示。

有关%Net.HttpRequest和身份验证的更多详细信息,请参阅使用Internet实用程序中发送HTTP请求中的提供登录凭据。

5.3 检查所需的元素和属性

默认情况下,Next()方法不检查是否存在与标记为Required的属性对应的元素和属性。要使读取器检查此类元素和属性的存在,请在调用Next()之前将读取器的CheckRequired属性设置为1。出于兼容性原因,此属性的默认值为0。

如果将CheckRequired设置为1,并且调用Next(),并且导入的XML缺少必需的元素或属性,则Next()方法会将sc参数设置为错误代码。例如:

SAMPLES>set next= reader.Next(.object,.status)
 
SAMPLES>w next
0
SAMPLES>d $system.Status.DisplayError(status)
 
ERROR #6318: Property required in XML document: ReqProp

5.4 处理意外的元素和属性

由于源XML文档可能包含意外的元素和属性,因此%XML.Adaptor提供了一些参数,用于指定导入此类文档时的反应方式。有关详细信息,请参阅将对象投影到XML中的专题。

5.5 控制导入空元素和属性的方式

当您以XML方式启用对象时,您可以指定如何将空值和空字符串投影到XML(请参见将对象投影到XML)。

其中一个选项是在启用XML的类中将XMLIGNORENNULL设置为“RUNTIME”(不区分大小写)。在这种情况下,当您使用%XML.reader以读取XML文件并创建InterSystems IRIS对象时,InterSystem IRIS将使用读取器的IgnoreNull属性的值来确定如何处理空元素或属性,如下所示:

  • 如果读取器的IgnoreNull属性为0(默认值),并且元素或属性为空,则相应的属性设置为等于$char(0)
  • 如果读取器的IgnoreNull属性为1,并且元素或属性为空,则未设置相应的属性,因此等于""

除非XMLIGNORENULL在启用XML的类中为“RUNTIME”,否则读取器的IgnoreNull属性无效;XMLIGNORENULL的其他可能值为0(默认值)、1和“INPUTONLY”;请参见将对象投影到XML。

5.5.1 示例:IgnoreNull为0(默认值)

首先,考虑以下类别

Class EmptyStrings.Import Extends (%Persistent, %XML.Adaptor)
{

Parameter XMLNAME="Test";

///Reader will set IgnoreNull property
Parameter XMLIGNORENULL = "RUNTIME";

Property PropertyA As %String;

Property PropertyB As %String;

Property PropertyC As %String;

Property PropertyD As %String(XMLPROJECTION = "ATTRIBUTE");

Property PropertyE As %String(XMLPROJECTION = "ATTRIBUTE");
}

还要考虑以下XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<Test PropertyD="">
  <PropertyA></PropertyA>
  <PropertyB xsi:nil="true" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</Test>

如果您创建一个%XML.Reader的实例,并使用它将此文件导入到上一个类中,则会得到以下结果:

  • PropertyA和PropertyD等于$char(0)。
  • 所有其他属性均等于""。

例如,如果您编写了一个例程来检查每个属性、检查其值并编写输出,则可能会得到以下内容:

PropertyA is $char(0)
PropertyB is null
PropertyC is null
PropertyD is $char(0)
PropertyE is null

5.5.2 示例:IgnoreNull为1

在前面示例的变体中,我们将读取器的IgnoreNull属性设置为1。在这种情况下,所有属性都等于""。

例如,如果您编写了一个例程来检查每个属性、检查其值并编写输出,则可能会得到以下内容:

PropertyA is null
PropertyB is null
PropertyC is null
PropertyD is null
PropertyE is null

5.6 其他有用的方法

有时,您可能需要使用以下%XML.Reader的附加方法:

  • 如果需要从XML源文档的开头重新开始读取,请使用Rewind()方法。此方法清除所有相关性。
  • 如果要显式关闭并清理导入处理程序,请使用Close()方法。导入处理程序将自动清理;包含此方法是为了向后兼容。

5.7 Reader 属性

您可以设置%XML.Reader的以下属性,以控制方法的整体行为:

  • 使用UsePPGHandler属性可以指定在%XML.Reader的实例在解析文档时是否使用进程专用全局变量。如果此属性为true,则实例将使用进程专用全局变量。如果此属性为false,则实例将使用内存。如果未设置此属性(或等于空字符串),则实例将使用默认值,通常为内存。

    使用Format属性可以指定XML文档的整体格式。指定以下值之一:

    • “literal”,默认值,用于本主题中的大多数示例。
    • “encoded”,按照SOAP 1.1标准中的描述进行编码。
    • “encoded12”,按照SOAP 1.2标准中的描述进行编码。

    请注意,您可以在OpenFile(), OpenStream(), OpenString(), 和OpenURL()方法中重写Format属性。

    除非使用Correlate()Next(),否则此属性无效。

  • 使用Summary属性可以强制读取器仅导入启用XML的对象的摘要字段。如将对象投影到XML中所述,对象的摘要由其XMLSUMMARY类参数指定,该参数指定为以逗号分隔的属性列表。

  • 使用IgnoreSAXWarnings属性指定读取器是否应报告SAX解析器发出的警告。

%XML.Reader还提供了属性,您可以使用这些属性检查正在阅读的文档:

  • Document属性包含一个%XML.Document实例,代表您正在阅读的整个已解析文档。有关%XML.Document的信息,请参阅类引用。
  • Node属性是一个字符串,表示XML文档的当前节点。请注意,0表示文档,即根元素的父元素。

有关EntityResolverSAXFlagsSAXSchemaSpec属性的信息,请参阅自定义SAX解析器的使用方式。请注意,SAXMask属性被忽略。

5.8 重新定义Reader如何处理相关对象

%XML.Reader的实例找到与启用XML的类相关的XML元素时,读取器会调用该类的XMLNew()方法,默认情况下该方法会调用%new()。也就是说,当阅读器找到一个相关的元素时,它会创建一个相关类的新对象。新对象将填充您从XML文档中读取的数据。

您可以通过在启用XML的类中(或者在您自己的自定义XML适配器中)重新定义XMLNew()来自定义此行为。例如,此方法可以打开该类的现有实例。然后,现有实例将接收您从XML文档中读取的数据。

以下示例显示了如何修改XMLNew()以使用XML文档中的新数据更新现有实例。

在这两个例子中,为了简单起见,我们假设XML文档中的节点包含一个ID,我们可以将该ID与类的扩展区中的ID进行比较。当然,我们可以通过其他方式将XML文档与现有对象进行比较。

5.8.1 当%XML.Reader调用XMLNew()

作为参考,在两种情况下,%XML.Reader中自动调用XMLNew()方法:

  • %XML.Reader,当您调用%XML.ReaderNext()方法时,调用XMLNew()。在您将XML元素(在外部文档中)与启用XML的类关联后。Next()方法从文档中获取下一个元素,调用XMLNew()来创建适当对象的实例,然后将该元素导入到对象中。
  • 类似地,%XML.Reader为相关XML元素的任何对象值属性调用XMLNew()

5.8.2 示例1:在启用XML的类中修改XMLNew()

首先考虑以下XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <Person>
    <IRISID>4</IRISID>
    <Name>Quine,Maria K.</Name>
    <DOB>1964-11-14</DOB>
    <Address>
      <City>Hialeah</City>
      <Zip>94999</Zip>
    </Address>
    <Doctors>
      <Doctor>
        <Name>Vanzetti,Debra B.</Name>
      </Doctor>
    </Doctors>
  </Person>
...

此文件映射到以下InterSystems IRIS类(部分显示):

Class GXML.PersonWithXMLNew Extends (%Persistent,%Populate,%XML.Adaptor)
{

Parameter XMLNAME="Person";

/// make sure this is the same as the XMLNAME of the property 
/// in this class that is of type %XML.Id
Parameter NAMEOFEXPORTID As %String = "IRISID";

Property IdForExport 
As %XML.Id(XMLNAME="IRISID",XMLPROJECTION="ELEMENT") [Private,Transient];

Property Name As %Name;

Property DOB As %Date(FORMAT = 5, MAXVAL = "+$h");

Property Address As GXML.Address;

Property Doctors As list Of GXML.Doctor;

}

在这个类中,IdForExport属性的目的是在导出这个类的对象时,将InterSystems IRIS内部ID投影到元素(IRISID)。(在这个特定的例子中,这使我们能够轻松地生成合适的文件进行导入。您的类不必包含这样的属性。)

NAMEOFEXPORTID参数用于指示导出此类对象时用于InterSystems IRIS ID的元素。这只是为了方便我们也添加到这个类中的自定义XMLNew()方法。该方法定义如下:

ClassMethod XMLNew(doc As %XML.Document, node As %Integer, 
contOref As %RegisteredObject = "") As GXML.PersonWithXMLNew
{
    Set id=""
    Set tmpnode=doc.GetNode(node)
    Do tmpnode.MoveToFirstChild()
    Do {
        //compare data node to the string given by the NAMEOFEXPORTID parameter
        //which indicates the XMLNAME of the ids for this object
        If tmpnode.NodeData=..#NAMEOFEXPORTID {
            //get the text from this node; this corresponds to an id in the database
            Do tmpnode.GetText(.id)}
        } While tmpnode.MoveToNextSibling()
    
    //if there is no id in the given node, create a new object
    If id="" {
        Write !, "Creating a new object..."
        Quit ..%New()}
    
    //open the given object
    Set result=..%OpenId(id)
    
    //if the id doesn't correspond to an existing object, create a new object
    If result=$$$NULLOREF {
        Write !, "Creating a new object..." 
        Quit ..%New()}

    Write !, "Updating an existing object..."

    Quit result
}

%XML.Reader,当它读取XML文档并将节点关联到GXML.PersonWithXMLNew时,调用此方法。此方法查看此类中NAMEOFEXPORTID参数的值,即IRISID。然后,它使用元素IRISID检查文档中的节点,并获取其值。

如果此ID对应于此类的现有对象,则该方法将打开该实例。否则,该方法将打开该类的一个新实例。在这两种情况下,实例都会接收XML文档中指定的属性。

最后,以下实用程序类包含一个方法,该方法打开XML文件并调用%XML.Reader

Class GXML.DemoXMLNew Extends %RegisteredObject
{

ClassMethod ReadFile(filename As %String = "c:\temp\sample.xml")
{
    Set reader=##class(%XML.Reader).%New()
    Set sc=reader.OpenFile(filename)
    If $$$ISERR(sc) {Do $system.OBJ.DisplayError(sc) Quit  }
    
    Do reader.Correlate("Person","GXML.PersonWithXMLNew")
    
    //loop through elements in file 
    While reader.Next(.person,.sc) {
        Write !,person.Name,!
        Set sc=person.%Save()
        If $$$ISERR(sc) {Do $system.OBJ.DisplayError(sc) Quit  }
    }

    Quit
}

}

当您运行前面的方法时,文件中的每个<Person>元素都会发生以下情况之一:

  • 将打开现有对象,使用文件中的详细信息进行更新并保存。
  • 或者创建一个新对象,其中包含文件中的详细信息。

例如:

GXML>d ##class(GXML.DemoXMLNew).ReadFile()
 
Updating an existing object...
Zampitello,Howard I.
 
Creating a new object...
Smyth,Linda D.
 
Creating a new object...
Vanzetti,Howard I.
 

5.8.3 示例2:在自定义XML适配器中修改XMLNew()

在第二个示例中,我们创建了一个自定义XML适配器来执行与第一个示例中所示相同的操作。适配器类别如下:

Class GXML.AdaptorWithXMLNew Extends %XML.Adaptor
{

/// make sure this is the same as the XMLNAME of the property in this class
/// that is of type %XML.Id
Parameter NAMEOFEXPORTID As %String = "IRISID";

Property IdForExport 
As %XML.Id(XMLNAME="IRISID",XMLPROJECTION="ELEMENT") [Private,Transient];

ClassMethod XMLNew(document As %XML.Document, node As %Integer, 
containerOref As %RegisteredObject = "") As %RegisteredObject 
[CodeMode=objectgenerator,GenerateAfter=%XMLGenerate,ServerOnly=1]
{
 If %compiledclass.Name'="GXML.AdaptorWithXMLNew" {
  Do %code.WriteLine(" Set id=""""")
  Do %code.WriteLine(" Set tmpnode=document.GetNode(node)")
  Do %code.WriteLine(" Do tmpnode.MoveToFirstChild()")
  Do %code.WriteLine(" Do {")
  Do %code.WriteLine("     If tmpnode.NodeData=..#NAMEOFEXPORTID ")
  Do %code.WriteLine("       {Do tmpnode.GetText(.id)}")
  Do %code.WriteLine(" } While tmpnode.MoveToNextSibling() ")
  Do %code.WriteLine(" If id="""" {")
  Do %code.WriteLine("  Write !,""Creating new object...""")
  Do %code.WriteLine("  Quit ##class("_%class.Name_").%New()}")
  Do %code.WriteLine(" set result=##class("_%class.Name_").%OpenId(id)")
  Do %code.WriteLine(" If result=$$$NULLOREF {")
  Do %code.WriteLine("     Write !,""Creating new object...""")
  Do %code.WriteLine("     Quit ##class("_%class.Name_").%New() }")
  Do %code.WriteLine(" Write !,""Updating existing object ...""")
  Do %code.WriteLine(" Quit result")

  }

 QUIT $$$OK
}

}

IdForExport属性和NAMEOFEXPORTID参数为导出子类对象时如何将InterSystems IRIS内部ID投影到元素建立了一个约定。其目的是,如果在子类中重新定义IdForExport,则相应地重新定义NAMEOFEXPORTID

在这个类中,XMLNew()方法是一个方法生成器。当编译这个类(或任何子类)时,InterSystemsIRIS将这里显示的代码写入这个方法的主体中。请参见定义和使用类中的方法生成器。

以下类扩展了我们的自定义适配器:

Class GXML.PersonWithXMLNew2 
Extends (%Persistent, %Populate, GXML.AdaptorWithXMLNew)
{

Parameter XMLNAME = "Person";

Property Name As %Name;

Property DOB As %Date(FORMAT = 5, MAXVAL = "+$h");

Property Address As GXML.Address;

Property Doctors As list Of GXML.Doctor;

}

当您运行前面显示的示例ReadFile方法时,对于文件中的每个<Person>元素,该方法要么创建并保存新记录,要么打开并更新现有记录。

5.9 其他示例

本节包含一些附加示例。

5.9.1 灵活的Reader类

以下示例显示了一个更灵活的读取器方法,它接受文件名、目录、类和元素作为输入参数。该方法保存它读取的每个对象。

Class Readers.BasicReader Extends %RegisteredObject
{

ClassMethod Read(mydir, myfile, class, element)
{
   set reader=##class(%XML.Reader).%New()
   if $extract(mydir,$length(mydir))'="/" {set mydir=mydir_"/"}
   set file=mydir_myfile
   set status=reader.OpenFile(file)
   if $$$ISERR(status) {do $System.Status.DisplayError(status)}

   do reader.Correlate(element,class)

   while reader.Next(.object,.status)
   {
      if $$$ISERR(status) {do $System.Status.DisplayError(status)}
      set status=object.%Save()
      if $$$ISERR(status) {do $System.Status.DisplayError(status)}
      }
}

}

请注意,当您读入Person对象时,您会自动读入其对应的Address对象。(当你保存Person对象时,你也会自动保存相应的Address对象。)这是一种相当粗糙的技术,只适用于批量加载数据;它不进行任何与现有数据进行比较或更新的尝试。

为了使用此方法,您需要一个支持XML的类,该类的投影与传入的XML文档相匹配。假设您有名为MyApp.PersonWithAddressMyApp.Address的启用XML的类。还假设您有一个XML文档,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<Root>
   <Person>
      <Name>Able, Andrew</Name>
      <DOB>1977-10-06</DOB>
      <Address>
         <Street>6218 Clinton Drive</Street>
         <City>Reston</City>
         <State>TN</State>
         <Zip>87639</Zip>
      </Address>
   </Person>
</Root>

要读取此文件中的对象并将其保存到磁盘,您需要执行以下操作:

 set dir="C:\XMLread-these"
 set file="PersonData.txt"
 set cls="MyApp.PersonWithAddress"
 set element="Person"
 do ##class(Readers.BasicReader).Read(dir,file,cls,element)

5.9.2 读取字符串

以下方法接受XML字符串、类和元素作为输入参数。它保存它读取的每个对象。

Class Readers.BasicReader Extends %RegisteredObject
{

ClassMethod ReadString(string, class, element)
{
   set reader=##class(%XML.Reader).%New()
   set status=reader.OpenString(string)
   if $$$ISERR(status) {do $System.Status.DisplayError(status)}

   do reader.Correlate(element,class)

   while reader.Next(.object,.status)
   {
      if $$$ISERR(status) {do $System.Status.DisplayError(status)}
      set status=object.%Save()
      if $$$ISERR(status) {do $System.Status.DisplayError(status)}
      }
}

}

要使用此方法,您需要执行以下操作:

 set cls="MyApp.Person"
 set element="Person"
 do ##class(Readers.BasicReader).ReadString(string,cls,element)

附:XML使用示例

util.XMLUtils

Class util.XMLUtils Extends (%RegisteredObject, %XML.Adaptor)
{

/// %XML.Reader
/// d ##class(util.XMLUtils).XMLReader()
ClassMethod XMLReader()
{
	s str =			"<Person>"
	s str =	str _		"<IDCard>340124202003054873</IDCard>"			
	s str =	str _		"<Name>wansk</Name>"
    s str =	str _		"<Relationships>"
    s str =	str _			"<Relationship>"
    s str =	str _				"<Relation>father</Relation>"
    s str =	str _				"<Name>wanyh</Name>"
    s str =	str _			"</Relationship>"
    s str =	str _			"<Relationship>"
    s str =	str _				"<Relation>mother</Relation>"
    s str =	str _				"<Name>lumina</Name>"
    s str =	str _			"</Relationship>"
    s str =	str _		"</Relationships>"
	s str =	str _	"</Person>"
	
	// Create an instance of %XML.Reader
	s reader = ##class(%XML.Reader).%New()
	
	// Begin processing of the str
	#; s status = reader.OpenStream(stream)
	s status = reader.OpenString(str)
	i $$$ISERR(status) {d $System.Status.DisplayError(status) q}
	
	// Associate a class name with the XML element name
	d reader.CorrelateRoot("MyApp.Person")
	
	// Read objects from xml str
	d reader.Next(.object, .status)
	i $$$ISERR(status) {d $System.Status.DisplayError(status) q}
	
	w "IDCard:", object.IDCard ,!
	w "Name:",  object.Name ,!
	w "RelationshipCount:",  object.Relationships.Count() ,!
	for i = 1 : 1 :object.Relationships.Count() {
		w "Relation:"_object.Relationships.GetAt(i).Relation ,!
		w "Name:"_object.Relationships.GetAt(i).Name ,!
	}
	
	#;	IDCard340124202003054873
	#;	Name:wansk
	#;	RelationshipCount2
	#;	Relation:father
	#;	Name:wanyh
	#;	Relation:mother
	#;	Name:lumina
}

/// %XML.TextReader
/// d ##class(util.XMLUtils).TextReader()
ClassMethod TextReader()
{
	s str =			"<Person>"
	s str =	str _		"<IDCard>340124202003054873</IDCard>"			
	s str =	str _		"<Name>wansk</Name>"
    s str =	str _		"<Relationships>"
    s str =	str _			"<Relationship>"
    s str =	str _				"<Relation>father</Relation>"
    s str =	str _				"<Name>wanyh</Name>"
    s str =	str _			"</Relationship>"
    s str =	str _			"<Relationship>"
    s str =	str _				"<Relation>mother</Relation>"
    s str =	str _				"<Name>lumina</Name>"
    s str =	str _			"</Relationship>"
    s str =	str _		"</Relationships>"
	s str =	str _	"</Person>"
	
	//check status
	#; s status = ##class(%XML.TextReader).ParseStream(Stream, .textreader)
	s status = ##class(%XML.TextReader).ParseString(str, .textreader)
	i $$$ISERR(status) {d $System.Status.DisplayError(status) q}
	
	//iterate through document, node by node
	while textreader.Read()
    {
	    w "seq: ", textreader.seq ,!
	    w "Path: ", textreader.Path ,!
	    w "NodeType: ", textreader.NodeType ,!
	    w "Name: ", textreader.Name ,!
	    w "Value: ", textreader.Value ,!
	}
	
	#;	seq: 1
	#;	Path: /Person
	#;	NodeType: element
	#;	Name: Person
	#;	Value: 
	#;	seq: 2
	#;	Path: /Person/IDCard
	#;	NodeType: element
	#;	Name: IDCard
	#;	Value: 
	#;	seq: 3
	#;	Path: /Person/IDCard
	#;	NodeType: chars
	#;	Name: 
	#;	Value: 340124202003054873
	#;	seq: 4
	#;	Path: /Person/IDCard
	#;	NodeType: endelement
	#;	Name: IDCard
	#;	Value: 
	#;  ......
}

}

MyApp.Person MyApp.Relationship

Class MyApp.Person Extends (%Persistent, %XML.Adaptor, %JSON.Adaptor) [ SqlTableName = MyAppPerson ]
{

Parameter XMLNAME = "Person";

Index IDCardKey On IDCard [ Unique ];

/// Person's IDCard number.
Property IDCard As %String(%JSONFIELDNAME = "IDNum") [ Required ];

/// Name of the person.
Property Name As %String;

/// Person's Relationships.
Property Relationships As list Of MyApp.Relationship;

Storage Default
{
<Data name="PersonDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>IDCard</Value>
</Value>
<Value name="3">
<Value>Name</Value>
</Value>
<Value name="4">
<Value>Relationships</Value>
</Value>
</Data>
<DataLocation>^MyApp.PersonD</DataLocation>
<DefaultData>PersonDefaultData</DefaultData>
<IdLocation>^MyApp.PersonD</IdLocation>
<IndexLocation>^MyApp.PersonI</IndexLocation>
<StreamLocation>^MyApp.PersonS</StreamLocation>
<Type>%Storage.Persistent</Type>
}

}


Class MyApp.Relationship Extends (%Persistent, %XML.Adaptor, %JSON.Adaptor) [ SqlTableName = Relationship ]
{

Parameter XMLNAME = "Relationship";

Property Relation As %String;

Property Name As %String;

Storage Default
{
<Data name="RelationshipDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Relation</Value>
</Value>
<Value name="3">
<Value>Name</Value>
</Value>
</Data>
<DataLocation>^MyApp.RelationshipD</DataLocation>
<DefaultData>RelationshipDefaultData</DefaultData>
<IdLocation>^MyApp.RelationshipD</IdLocation>
<IndexLocation>^MyApp.RelationshipI</IndexLocation>
<StreamLocation>^MyApp.RelationshipS</StreamLocation>
<Type>%Storage.Persistent</Type>
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值