%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 总体方法结构
您的方法应按以下顺序执行部分或全部操作:
-
创建
%XML.Reader
的实例。 -
(可选)指定此实例的“格式”属性,以指定要导入的文件的格式;请参考 Reader 属性。
默认情况下,InterSystems IRIS假设XML文件是文字格式的。如果您的文件是SOAP编码的格式,则必须指出这一点,以便能够正确读取文件。
-
(可选)设置此实例的其他属性;请参考 Reader 属性。
-
打开源数据。要执行此操作,请使用以下
%XML.Reader
方法之一:-
OpenFile() — 打开一个文件。
-
OpenStream() — 打开流。
-
OpenString() — 打开一个字符串。
-
OpenURL() — 打开一个URL。
如果URL使用HTTPS,请参阅访问HTTPS URL下的文档。
在每种情况下,您都可以选择为该方法指定第二个参数,以覆盖Format属性的值。
-
-
将此文件中的一个或多个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)
- 使用
-
实例化类实例,如下所示:
- 如果使用
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,请执行以下操作:
-
使用管理门户创建SSL/TLS配置,该配置包含所需连接的详细信息。有关信息,请参阅InterSystems TLS指南。
这是一次性的步骤。
-
使用
%XML.reader
时,请设置reader实例的SSLConfiguration
属性。对于该值,请指定在上一步中创建的SSL/TLS配置的名称。
或者,当您使用%XML.Reader
时,也可以执行以下操作:
- 创建
%Net.HttpRequest
的实例。 - 将该实例的
SSLConfiguration
属性设置为等于您在管理门户中创建的SSL/TLS配置的配置名称。 - 使用
%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表示文档,即根元素的父元素。
有关EntityResolver
、SAXFlags
和SAXSchemaSpec
属性的信息,请参阅自定义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.PersonWithAddress
和MyApp.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 ,!
}
#; IDCard:340124202003054873
#; Name:wansk
#; RelationshipCount:2
#; 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>
}
}