Chinaunix首页 | 论坛 | 博客
  • 博客访问: 668561
  • 博文数量: 77
  • 博客积分: 6055
  • 博客等级: 准将
  • 技术积分: 617
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-17 02:15
个人简介

一句话太少

文章分类

全部博文(77)

文章存档

2018年(1)

2015年(1)

2014年(1)

2013年(6)

2011年(1)

2010年(1)

2009年(4)

2008年(2)

2007年(21)

2006年(39)

我的朋友

分类:

2006-12-30 10:07:56

本文将给出现实生活中如何在一个BEA Tuxedo 8.1应用程序中使用XML的例子。假如您持有一个定位于通过交换XML数据来集成某些应用程序的项目,那么对于了解“我如何实现这一点”,本文将是一个很好的介绍。
  本文的对象是那些已经对Tuxedo有所了解,但从未使用过Tuxedo内嵌的XML缓冲功能的架构师和开发人员。

Tuxedo中XML的历史
  
如今随着XML逐渐成为主流的数据格式之一,自然而然地Tuxedo将之作为一种基本缓冲类型予以支持。    Tuxedo 7.1 引入了XML缓冲类型,但迄今为止对于Tuxedo中的XML并无较多的论述。Tuxedo 7.1中并未携带真正的XML API,因为通常认为将开发人员自己最喜欢的DOM或SAX实现合并到其应用程序中是其自身的责任。
  现在,由于在Tuxedo内嵌了Xerces 1.7 版本(来自Apache XML项目)库,所以伴随Tuxedo 8.1提供了一套真正的XML API。8.1版本还包含了一个称为xmlstockapp(xml股票程序)的XML示例。
  这一点为开发人员增加了许多XML功能,例如XML解析,XML树遍历或构建,以及XML格式化,从而不需要使用外部的产品。

何为XML?
  
XML经常作为一种“语言”被提及,然而它实际上是一种用于描述文本缓冲区中的层次结构数据集的标准格式。这种数据格式可以通过一种加强数据结构和层次关系的本地语法(称为DTD)来进行控制。

我如何在XML中进行编码?
  
XML并非一种语言,而是一种数据格式。编码意味着使用一种API,从一个纯文本缓冲区到数据树来回进行数据转换,或者对数据树进行遍历或操纵。想要了解如何使用XML进行编码,请参看下文“……一般任务”一节。

有哪些可用的API对XML进行操纵作?
  
目前存在数种标准API对XML进行操纵,最著名的是DOM,SAX和XPATH。所有的API都针对不同语言具有不同的实现,但是本文仅仅关注Tuxedo 8.1产品中包含的Xerces的C/C++实现。

我应在何时使用XML?
  
由于XML是一种标准的有组织的格式,如今它已经成为一种广为使用的便利格式来在不同系统间进行数据交换。即使结构上(DTD)略作改动来添加一些子节点或新属性,XML的自描述性和结构化的方式也有助于数据的理解。XML也是易于阅读的,而二进制数据则不然。

然而XML仍具有一些缺点:

  • 它使您的数据大幅膨胀(每个域将增加一个20字节左右的标题,并且所有的二进制域将扩大为相应的字符串表达)。
  • 在进行解析和格式化时它增加了CPU的开销,尤其是在解析和检验文档是否是“格式良好”时。
  • 其API均比较复杂。

  因此XML是一种理想的集成语言,但是在系统内部您可能并非处处都需要使用它。在Tuxedo中它通常用于同外部系统发送和接收数据。

对于使用DOM API处理XML数据的程序的一般任务:
  
这些任务并非只针对Tuxedo,而是适用于所有使用DOM处理XML数据的程序。

初始化Xerces
  一旦您希望使用Xerces API进行工作就必须强制进行初始化:

/* initialise xerces */
try
{
   XMLPlatformUtils::Initialize ();
}
catch (const XMLException & toCatch)
{
   char *pMsg = XMLString::transcode (toCatch.getMessage ());
   userlog ("Error during Xerces-c Initialization.\n"
           "  Exception message: %s", pMsg);
   delete[]pMsg;
   return -1;
}

解析XML文本
  当XML 数据被接收时,它通常是一个文本缓冲区或一个文件。DOM API是一种用于处理数据节点树的API,这种节点树通常由包含了属性和其他元素的元素构成。一个程序可以通过递归扫描元素节点来遍历DOM树。
  如果一个程序希望通过层次结构,元素名称或者属性来访问缓冲区内的数据,该缓冲区首先需要被读入并转化为一棵数据树。这一过程被称为解析。要执行文本解析您需要实现一个来源类(LocalFileInputSource或MemBufInputSource)来容纳将要解析的文本,并使用DOMParser的实现来进行解析。

#include
#include
#include
#include
#include
#include

#include
#include

  /* parse a XML file */
char *xmlFileName = "./myfile.xml";
DOMParser *parser = 0;
DOM_Document document;
DOM_Element topLevel;

  LocalFileInputSource source (XMLString::transcode (xmlFileName));
 //
 //  Create our parser, then set the parsing options.
 //  discovers errors during the course of parsing the XML document.
 //
parser = new DOMParser ();
parser->setValidationScheme (DOMParser::Val_Never);
parser->setDoNamespaces (false);
parser->setDoSchema (false);
parser->setValidationSchemaFullChecking (false);
parser->setCreateEntityReferenceNodes (false);
parser->setToCreateXMLDeclTypeNode (true);
//
//  Parse the XML file, catching any XML exceptions that might propagate
//  out of it.
//
try  {
   parser->parse (source);
   int errorCount = parser->getErrorCount ();
   if (errorCount > 0)      {
      printf("%d error(s) occured during parsing config\n", errorCount);
      goto clean;
     }
}
catch (const XMLException & e)  {
   printf("An error occured during parsing \n   Message: %s\n"
          "", e.getMessage ());
   goto clean;
}
catch (const DOM_DOMException & e)  {
   printf("A DOM error occured during parsing config\n"
           "Exception code: %d\n", e.code);
   goto clean;
}
catch (...)  {
   printf ("An error occured during parsing config\n");
   goto clean;
}

处理解析错误
  一旦遇到解析错误,获取错误发生的行号和列号来代替仅仅进行错误计数(或获取一个致命异常)是更好的方式。这种做法能够通过安装错误处理来轻松实现:

#include
class ExampleErrorHandler: public ErrorHandler {
   virtual void anyError(char* type, const SAXParseException& exception) ;
public:
   /** Default constructor */
   ExampleErrorHandler(){};
   /** Destructor */
   ~ExampleErrorHandler() {};
   void warning(const SAXParseException& exception){anyError("warning", exception); };
   void error(const SAXParseException& exception) {anyError("error", exception); };
   void fatalError(const SAXParseException& exception){anyError("fatal error", exception); };
   void resetErrors() {};
};

void ExampleErrorHandler::anyError(char* type, const SAXParseException& exception){
      printf("Parser %s line %d column %d: %ls %ls : %ls", type,
             exception.getLineNumber () ,
             exception.getColumnNumber () ,
             exception.getPublicId  () ,
             exception.getSystemId () ,
             exception.getMessage ());
}

在解析代码中:

ExampleErrorHandler handler;
parser = new DOMParser ();
parser->setErrorHandler(&handler);
//
//  Parse the XML file, catching any XML exceptions that might propagate
//  out of it.
//
try  {
   parser->parse (source);
}

创建一棵DOM树
  您可以按这种方式创建一棵DOM树。它创建了根元素。

/* creates an empty dom tree */
DOM_DOMImplementation impl;
DOM_Document doc = impl.createDocument (0,  // root element namespace URI.
                                    rootname,  // root element name
                                    DOM_DocumentType ());// document type object (DTD).
/* fetch the root element */
DOM_Element rootElem = doc.getDocumentElement ();

对一个元素添加子元素

//Add new (empty) Element to the root element
     DOM_Element parentNode = …;// parent is known
     DOM_Element prodElem = doc->createElement (tagName);
     parentNode->appendChild (prodElem);

删除一个子元素

     parentNode->removeChild (prodElem);

修改DOM树元素的标签名称
  一旦元素被创建您就不可以修改其标签名称。

遍历一个元素的子元素
  既然您已经创建了一个XML元素节点,您(可能)希望访问其子元素:

DOM_Element parent = …; // parent is known
DOM_Node child;
child = parent.getFirstChild ();
while  (child != 0)    {
   //work with child 
   … 
   //pickup next child
   child  = child.getNextSibling ();
}

按类型过滤子节点
  您可能希望忽略某些节点,在这种情况下,检查节点类型:

     switch (child.getNodeType ())
      {
      case DOM_Node::ELEMENT_NODE:
            …
        break;
      case DOM_Node::ATTRIBUTE_NODE:
            …
        break;
      case DOM_Node::TEXT_NODE:
            …
        break;
      case DOM_Node::CDATA_SECTION_NODE:
            …
        break;
      case DOM_Node::ENTITY_REFERENCE_NODE:
            …
        break;
      case DOM_Node::ENTITY_NODE:
            …
        break;
      case DOM_Node::PROCESSING_INSTRUCTION_NODE:
            …
        break;
      case DOM_Node::COMMENT_NODE:
            …
        break;
      case DOM_Node::DOCUMENT_NODE:
            …
        break;
      case DOM_Node::DOCUMENT_TYPE_NODE:
            …
        break;
      case DOM_Node::DOCUMENT_FRAGMENT_NODE:
            …
        break;
      case DOM_Node::NOTATION_NODE:
            …
        break;
      case DOM_Node::XML_DECL_NODE:
            …
        break;
      default:
            …
      }

获得一个元素的值
  一个元素内的值存储在一个文本子节点中:

DOM_Element parent = …; // parent is known
DOM_Node child;
DOM_Text value;
child = parent.getFirstChild ();
while  (child != 0)    {
   //work with child 
   if (child.getNodeType () == DOM_Node::TEXT_NODE)    {
     value = (DOM_Text &) child;
     break;
   }     
   //pickup next child
   child  = child.getNextSibling ();
}

DOMString unicodeValue = value.getData ();
//if you need the ascii value
char* asciiValue = unicodeValue.transcode ();
//work with your value

//free the value
delete []asciiValue ;

更改DOM树元素的值
  另外,如果存在的话,不要忘记删除之前的值:

DOM_Element parent = …; // parent is known
DOM_Node child;
DOM_Text value;
bool childFound = false;
child = parent.getFirstChild ();
while  (child != 0)    {
   //work with child 
   if (child.getNodeType () == DOM_Node::TEXT_NODE)    {
     value = (DOM_Text &) child;
     childFound = true;
     break;
   }  
   //pickup next child
   child  = child.getNextSibling ();
}
//now , maybe create a text node
if (!childFound) {
        value = doc->createTextNode ();
        parent.appendChild (value);
}
DOMString unicodeValue(asciiValue);
value.setData(unicodeValue);

添加或修改一个元素的属性
  要添加(或设置)一个元素属性的值,使用以下方法:

     DOMString unicodename(asciiname);
     DOMString unicodevalue(asciivalue);
     DOM_Element element = …;// element is known
     //Add new attribute to the element
     element->setAttribute(unicodename, unicodevalue);

删除一个元素的属性
  要删除一个元素的属性,使用以下方法: [I.L.H1] 

     DOMString unicodename(asciiname);
     DOM_Element element = …;// element is known
     //Add new attribute to the element
     element->removeAttribute(unicodename);

遍历一个元素的属性
  要浏览一个元素的所有属性,您可以按以下方法加以实现:

//loop through this element attributes and fill the config structure
     DOMString unicodename;
     DOMString unicodevalue;
     DOM_NamedNodeMap attributes;
     DOM_Element element = …;// element is known
     attributes = element.getAttributes ();
     int attrCount;
     attrCount = attributes.getLength ();
     for (i = 0; i < attrCount; i++)    {
         DOM_Node attribute = attributes.item (i);
         //work with the attribute
         unicodename = attribute.getNodeName ();
         unicodevalue= attribute. getNodeValue ();
         //if need ascii values, get them
         char* asciiname= unicodename.transcode ();
         char* asciivalue = unicodevalue.transcode ();
         …
         //but don't forget to release them
         delete []asciiname;
         delete []asciivalue;
     }

将DOM树作为文本缓冲区打印输出
  并没有直接的方法来将DOM树格式化为XML文本缓冲区。最简单的方法是参考Xerces 1.7中DOMPrint的示例,或者查看一下本文附带的XFML库中的XMLimplementation.hxx文件。
  具体的想法是实现一个函数,该函数将遍历所有节点并将节点及其属性打印输出。
  Tuxedo中的XML示例:
  所有附带的代码示例均经过编译,并在Microsoft Windows NT下使用。由于示例中依赖的库均已在Unix中经过编译,移植到Unix的工作量应该很小。

FML32向XML通用转换(含代码)
  我们希望执行一个基本的FML32向XML的转换,使用与XML元素名称相同的域名,来像XML文本节点那样包含相应的值。本示例假设我们使用的是8位UTF字符,不需要字符数据转换。
  将一个FML32转换为一个XML缓冲区非常直截了当,尤其在介绍完内嵌的FML32特性后,这一特性取消了对XML树的深度约束。
  该库以C++类的形式加以实现,提供了两种主要方法来进行XML缓冲区到FML32缓冲区的转换(XMLToFML32)和FML32缓冲区到XML缓冲区的转换(FML32ToXML)。

class XMLTransformer
{
public:
   /**
    *  DOMTransformer : constructor.

    * @throws nothing
    */
      XMLTransformer();
   /**
    *  ~DOMTransformer : destructor.

    * @throws nothing
    */
      virtual ~XMLTransformer();
   /**
    *  XMLToFML32 : makes a FML32 buffer from a XML buffer.

    * The method allocates a FML32 buffer and fills it with the data from the XML buffer.

    * @param xml_in: the XML buffer in (in text format).
    * @param fbfr_out : the FML32 buffer returned. Must be set to NULL before calling or will be tpreallocated.
    * @returns : true if successful.
    * @throws nothing
    */
      bool XMLToFML32( char* xml_in, FBFR32*& fbfr_out, char* &rootname = *(char**)0);
      /**
    *  FML32ToXML: makes a FML32 buffer from a XML buffer.

     * The method allocates a FML32 buffer and fills it with the data from the XML buffer.

    * @param fbfr_in: the FML32 buffer in.
    * @param xmlstr_out : the returned XML buffer (in text format). Must be set to NULL before calling or will be tpreallocated.
    * @returns : true if successful.
    * @throws nothing
    */
      bool FML32ToXML( FBFR32*fbfr_in, char*&xmlstr_out, char* rootname=0);
     /**
·      FML32 helper functions. Reallocate space in case buffer is too small

·      */
static int Fappend32(FBFR32 *&fbfr, FLDID32 fieldid, char *value, FLDLEN32 len)  ;
static int Fadd32(FBFR32 *&fbfr, FLDID32 fieldid, char *value, FLDLEN32 len)  ;
static int Findex32(FBFR32 *&fbfr, FLDLEN32 len)  ;
};

  该类中开发的所有代码均使用Tuxedo分发中Xerces 1.7的标题和库。如果您的目标并非创建FML32缓冲区,您仍然可以检查该代码并获得解析,遍历和打印输出XML缓冲区的灵感。

POP网关(含代码)
  为了从一个电子邮件pop帐户中导入XML数据,有必要将Tuxedo系统中的数据以FML32缓冲区的形式注入到目标服务中。错误消息(例如,XML解析失败的消息)都被发送往另一台处理文本缓冲区的服务器。本示例使用了前面通用的XML向FML32转换的示例。
  该服务期望一个FML32的输入信息,而后将建立一个XML缓冲区来放入消息体。该服务还使用了一些特殊的FML32域来存储一些提取出的头值:

POP_HEADERNAME             all header lines are extracted and are stored
POP_HEADERVALUE            two fields in the order met
POP_FROM                  copy of the 'from' header value
POP_TO                      copy of the 'to' header value
POP_CC                             copy of the 'cc:' header value
POP_SUBJECT             copy of the subject header value
POP_XMLROOT            tag name of the XML root element

  该Tuxedo服务器的配置信息包含在一个XML文件中,此文件以参数形式传递给CLOPT。
  这个XML文件包含了连接服务器所必需的所有信息,参见以下dtd文件以查看所有可能的值。

  verbose                    prints debug information in the ULOG file
  server                     dns name (or IP ) of the pop server
  port                       optional port of the pop server
  username                   username for pop connection
  password                   password for pop connection
  leaveOnServer              requests to leave mainls on the server
  forwardTo                  service name to forward the e-mails
  forwardErrorTo             service name to forward the e-mails in error (not XML?)
  tuxPollTime                sleep time (seconds ) between service invocations
  popPollTime                time (seconds ) between pop server queries

  注意如果您将电子邮件保留在服务器上,pop服务器将对每封邮件仅读取一次,原因是pop服务器将其已经获取的电子邮件列表记录在一个状态文件中。
  这些代码使用了一个(略有修改的)名为libESMTP的GPL 1.0版本库。

SMTP网关(含代码)
  假设我们希望将XML数据导出到某些电子邮件地址。这些数据在Tuxedo端以FML32缓冲区的形式加以处理并导出为XML。本示例使用了之前通用的FML32向XML转换的例子。
  该服务期望一个FML32的输入,而后将创建一个XML缓冲区以放置消息体。该服务还使用了一些特殊的FML32域来创建头值:

SMTP_TO
SMTP_CC
SMTP_FROM
SMTP_SUBJECT
SMTP_XMLROOT

  如果这些值均未被发现,服务将从配置文件中获取相应值。所有TO和CC目标将在随后得以发送这些消息。
  该Tuxedo服务器的配置信息包含在一个XML文件中,此文件以参数形式传递给CLOPT。
  这个XML文件包含了连接服务器所必需的所有信息,参见以下dtd文件以查看所有可能的值。

verbose                prints debug information in the ULOG file
server                   dns name (or IP ) of the SMTP server
port                      optional port of the SMTP server
username              username for SMTP connection
password              password for SMTP connection
from                     from target  in messages headers when none is provided
default_to             'to' target in message headers when none is provided with SMTP_TO
default_cc             'cc' target in message headers when none is provided with SMTP_CC
default_subject     subject of  message when none is provided with SMTP_SUBJECT

  这些代码使用了一个(略有修改的)名为“libspopc”()的GPL库。

SOAP服务器
  还有一个Tuxedo客户端(或伪服务器,像TMQFORWARD 服务器那样通过Tuxedo客户端提供“主机服务”)作为SOAP服务器运行的选项。这仅仅是一种概念,在本文中并没有实现。

其他网关
  如果第三方产品自然地以XML形式进行“通信”,它们应当可以很容易地同Tuxedo系统进行互操作,从而高效地进行集成。

原文出处:http://dev2dev.bea.com/products/tuxedo/articles/tux_gibart.jsp
阅读(2880) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~