在传递数据时,XML和JSON是最常用的数据格式,SQL Server从很早的版本就开始支持XML格式,而对于JSON格式,SQL Server从2016版本开始支持。大多数数据库系统并没有升级到SQL Server 2016版本,因此在传递格式化的数据时,通常还是使用XML格式。对我而言,查询和解析XML格式的数据需要掌握的知识点较多,MSDN上关于XML的文档,又试图把XML的各个方面都讲解地清清楚楚,以至于内容冗杂,使学习过程变得困难。我十分不喜欢学习这些不常用的数据结构,再说,在平时的数据库开发中,用到XML的地方也很少,可是,一旦在应用程序中用到XML,就只有头疼的份了,既然避不开XML,那就用最简单的方法学习它,了解它,使用它,以备不时之需。写这篇文章,就是以最简单的方式,分享XML最常用的使用方法。

一,XML数据格式的简单介绍

1,最简单的XML格式

XML数据最简单的格式是:

  • 开始标签:<tag>
  • 标签的属性,属性值用双引号:<tag id="1" name="azure">,在单个节点中,属性名不能重复,属性之间使用空格分隔,在开始标签中,才能设置属性;
  • 结束标签:</tag>,结束标签不能有属性;
  • 子节点:在开始标签和结束标签,可以包含节点,叫做子节点;
  • 节点值:在开始标签和结束标签的标量值,叫做节点值;

2,使用字符串对XML数据赋值

数据类型XML用于存储XML格式化的文本数据,在本例中,声明一个XML类型的变量 @xml,并赋值,后文示例都使用该变量用于数据查询。

declare @xml xml
set @xml='
<Expression ID="1" TaxonomyID="1">
<SubExpression ID="1" OperandType="Tag" Operator="and">
<Oprand ID="268819" Name="abuse" />
<Oprand ID="277029" Name="mongohq" />
<Oprand ID="516813" Name="access" />
</SubExpression>
<SubExpression ID="2" OperandType="Tag" Operator="and">
<Oprand ID="283839" Name="reviews" />
<Oprand ID="697348" Name="retention" />
</SubExpression>
<SubExpression ID="3" OperandType="Tag" Operator="not">
<Oprand ID="281556" Name="richfaces" />
<Oprand ID="2993766" Name="rgp" />
</SubExpression>
</Expression>'

二,XPath路径表达式

XPath 使用路径表达式在 XML 文档中选取节点,节点是通过沿着路径选取的,XPath是查询XML数据时必备的参数。

常用的路径表达式是:

  • .   :选取当前节点;
  • ..  :选取当前节点的父节点;
  • /   :从根节点开始;
  • //  :从匹配选择的节点开始选取,而不考虑其位置;
  • *  :通配符,匹配任意字符,或任意节点;
  • node()  :匹配任意节点,跟通配符 * 功能相似;
  • @PropertyName :选取属性;

在路径表达式中,跟节点的选取有关的表达式是:

  • NodeName:选取指定节点名及其所有子节点;
  • NodeName[N]:选取指定节点集合的第N个节点;
  • NodeName[@Name]:选取当前节点中带有指定属性的节点;

三,XML数据的查询(query()函数

@xml.query(’xpath‘)函数,参数是路径表达式,返回XML数据类型的结果,该XML是非类型化(untyped)的。

1,选取节点及其子节点

示例中,从根节点Expression开始,选取SubExpression节点及其子节点:

select @xml.query('/Expression/SubExpression')

query()函数返回的结果如下,该查询结果是非类型化的XML数据。

<SubExpression ID="1" OperandType="Tag" Operator="and">
<Oprand ID="268819" Name="abuse" />
<Oprand ID="277029" Name="mongohq" />
<Oprand ID="516813" Name="access" />
</SubExpression>
<SubExpression ID="2" OperandType="Tag" Operator="and">
<Oprand ID="283839" Name="reviews" />
<Oprand ID="697348" Name="retention" />
</SubExpression>
<SubExpression ID="3" OperandType="Tag" Operator="not">
<Oprand ID="281556" Name="richfaces" />
<Oprand ID="2993766" Name="rgp" />
</SubExpression>

2,选取指定节点的所有子节点集合

select @xml.query('/Expression/SubExpression/node()')
select @xml.query('/Expression/SubExpression/*')

结果集是SubExpression节点下的所有子节点:

<Oprand ID="268819" Name="abuse" />
<Oprand ID="277029" Name="mongohq" />
<Oprand ID="516813" Name="access" />
<Oprand ID="283839" Name="reviews" />
<Oprand ID="697348" Name="retention" />
<Oprand ID="281556" Name="richfaces" />
<Oprand ID="2993766" Name="rgp" />

四,XML数据的查询(value()函数)

@xml.value('xpath','sql_data_type'),返回XML数据中单个属性的标量值,在使用value()函数时,xpath 参数必须指定返回的是单个值,而value()函数不会去check返回值的数量。

一般情况下,即使xml数据只有一个属性值,静态类型化(Static typing)要求,xpath表达式也必须显式指定返回单个标量值,因此,必须指定在xpath函数的末尾添加”[1]“,通常的xpath表达式是”(xpath)[1]“。

select @xml.value('(/Expression/SubExpression[1]/@ID)[1]','int')
select @xml.value('(/Expression/SubExpression/@ID)[1]','int')

五,XML数据的查询(nodes()函数) 

@xml.nodes ('xpath') 函数返回节点的集合,用于把XML数据转换为关系数据表,返回的每一个行都是XML数据类型,语法是:

nodes ('xpath') as table(column)  

通过nodes()函数,返回SubExpression节点及其属性,由于单个节点中,属性名不可能重复,因此,在nodes()函数返回的单个节点中,不需要通过xpath路由,直接获取当前节点的属性值,这样,可以在xpath表达式中直接指定属性,不需要显式以“[1]”结尾。

示例代码如下,在value()函数中,直接指定属性值,表示获取当前节点的属性值:

select t.v.query('.') as SubExpression
,t.v.value('@ID','int') as SubExpressionID
,t.v.value('@OperandType','varchar(16)') as OperandType
,t.v.value('@Operator','varchar(16)') as Operator
from @xml.nodes('/Expression/SubExpression') as t(v)

通过cross apply 连接操作,把SubExpression节点下的所有数据都转换为关系型数据,并把该数据存储到临时数据表#Expressions中:

;with cte_Expressions as
(
select e.v.query('.') as Expression
,e.v.value('@ID','int') as ExpressionID
,e.v.value('@TaxonomyID','int') as TaxonomyID
from @xml.nodes('/Expression') as e(v)
)
,cte_SubExpression as
(
select e.ExpressionID
,e.TaxonomyID
,se.SubExpression
,se.SubExpressionID
,se.OperandType
,se.Operator
from cte_Expressions e
cross apply
(
select t.v.query('.') as SubExpression
,t.v.value('@ID','int') as SubExpressionID
,t.v.value('@OperandType','varchar(16)') as OperandType
,t.v.value('@Operator','varchar(16)') as Operator
from e.Expression.nodes('/Expression/SubExpression') as t(v)
) as se
)
select p.TaxonomyID
,p.ExpressionID
,p.SubExpressionID
,p.OperandType
,p.Operator
,d.OperandID
,d.OperandName
from cte_SubExpression p
cross apply
(
select t.v.value('@ID','int') as OperandID
,t.v.value('@Name','varchar(32)') as OperandName
from p.SubExpression.nodes('/SubExpression/Oprand') as t(v)
) as d

六,把行集数据转化为XML数据(for xml path)

把行集数据转化为XML数据,需要用到for xml path子句,该子句的特点是:

  • path('root') 子句用于指定根节点;
  • select子句的字段别名用于指定属性,别名中必须使用@符号标识出属性名,例如:'@PropertyName';
  • 在select 子句中,如果不在别名中把字段指定为属性,那么该字段的值作为节点值,节点值分为标量类型和XML类型;
    • 对于标量类型,节点值是标量值;
    • 对于XML类型,节点值是子节点的集合;

例如,要把数据转换为如下的关系型数据结构,其SubExpression字段是一个非类型化的XML数据,要完成这样的数据转换,必须使用for xml path子句和cast()类型转换:

<SubExpression ID="1" OperandType="Tag" Operator="not">
<Oprand ID="268819" Name="abuse" />
<Oprand ID="277029" Name="mongohq" />
<Oprand ID="516813" Name="access" />
</SubExpression>
<SubExpression ID="2" OperandType="Tag" Operator="not">
<Oprand ID="283839" Name="reviews" />
<Oprand ID="697348" Name="retention" />
</SubExpression>
<SubExpression ID="3" OperandType="Tag" Operator="not">
<Oprand ID="281556" Name="richfaces" />
<Oprand ID="2993766" Name="rgp" />
</SubExpression>

使用类型转换的目的,是为了把for xml path返回的字符串转换成XML数据类型,这样,就能以XML格式嵌入到上次的for xml path的结构中,作为子节点:

;with cte_Expressions as
(
select distinct ExpressionID
,TaxonomyID
from #Expressions with(nolock)
)
,cte_SubExpressions as
(
select o.ExpressionID
,o.SubExpressionID
,o.OperandType
,o.Operator
from #Expressions o with(nolock)
group by o.ExpressionID
,o.SubExpressionID
,operandType
,o.Operator
)
select e.TaxonomyID as TaxonomyID
,e.ExpressionID as ExpressionID
,cast(
(
select o.SubExpressionID as '@ID'
,o.OperandType as '@OperandType'
,case o.Operator when '&' then 'and' else 'not' end as '@Operator'
,cast((
select op.OperandID as '@ID'
,op.OperandName as '@Name'
from #Expressions op with(nolock)
where o.ExpressionID=op.ExpressionID
and o.SubExpressionID=op.SubExpressionID
for xml path('Oprand')
)as xml)
from cte_SubExpressions o
where o.ExpressionID=e.ExpressionID
for xml path('SubExpression')
) as xml)
as SubExpressions
from cte_Expressions e

还有两个函数modify()和exist(),用于XML数据的修改和检查,由于在我当前接触的项目中,没有用到过,我就不写了。

到此,文章也该结尾了,XML的极简用法已经总结了很多,在以后工作中国,如果用到XML时,翻开这篇文章,能够快速解决XML常见的数据查询和解析问题,这样就足够了。

参考文档:

Use PATH Mode with FOR XML

XML Data (SQL Server)

Security7:View Usage的更多相关文章

  1. iOS应用架构谈(二):View层的组织和调用方案(上)

    OS客户端应用架构看似简单,但实际上要考虑的事情不少.本文作者将以系列文章的形式来回答iOS应用架构中的种种问题,本文是其中的第二篇,主要讲View层的组织和调用方案.上篇主要讲View层的代码结构. ...

  2. iOS应用架构谈(二):View层的组织和调用方案(中)

    iOS客户端应用架构看似简单,但实际上要考虑的事情不少.本文作者将以系列文章的形式来回答iOS应用架构中的种种问题,本文是其中的第二篇,主要讲View层的组织和调用方案.中篇主要讨论MVC.MVCS. ...

  3. Android进阶笔记05:View、SurfaceView 和GLSurfaceView 的关系和区别

    1.  Android游戏当中主要的除了控制类外就是显示类View.SurfaceView是从View基类中派生出来的显示类.android游戏开发中常用的三种视图是:    (1) view.Sur ...

  4. 自定义控件(视图)1期笔记01:View 和 ViewGroup

    1.View 和 ViewGroup 图解关系: 2. View 和 ViewGroup 关系和作用: (1) 关系: • 继承关系 • 组合关系 (2) 作用:      • View的作用: 提供 ...

  5. PureMVC(JS版)源码解析(九):View类

    在讲解View类之前,我们先回顾一下PureMVC的模块划分:      在PureMVC中M.V.C三部分由三个单例类管理,分别是Model/View/Controller.PureMVC中另外一个 ...

  6. xUtils3源码分析(一):view的绑定

    概述 xUtils3是国人开发的一款功能丰富的Android快速开发框架,值得研究下.zip包下载:[ZIP]xutils主要分以下几个模块 视图绑定模块 网络请求模块 数据库模块 图片加载模块 我们 ...

  7. 【转】请求处理机制其三:view层与模板解析

     进入 View 了 如果处理过程这时候还在继续的话,处理器会调用 view function.Django 中的 Views 不很严格因为它只需要满足几个条件: 必须可以被调用. 必须接受 djan ...

  8. 小程序实践(六):view内部组件排版

    涉及知识点: 1.垂直排列,水平排列 2.居中对齐 示例: 1.默认排版 , 一个父组件里面两个子view 显示效果: 2.先给父view设置一个高度和颜色值,用于看效果 3.实现水平排列和垂直排列的 ...

  9. Android精通:View与ViewGroup,LinearLayout线性布局,RelativeLayout相对布局,ListView列表组件

    UI的描述 对于Android应用程序中,所有用户界面元素都是由View和ViewGroup对象构建的.View是绘制在屏幕上能与用户进行交互的一个对象.而对于ViewGroup来说,则是一个用于存放 ...

随机推荐

  1. c#取得应用程序根目录

    1.取得控制台应用程序的根目录方法 方法1.Environment.CurrentDirectory 取得或设置当前工作目录的完整限定路径方法2.AppDomain.CurrentDomain.Bas ...

  2. iOS 为键盘添加隐藏按钮

    // 为键盘添加隐藏按钮 UIToolbar * backView = [[UIToolbar alloc]initWithFrame:CGRectMake(, , , )]; [backView s ...

  3. 【NEUQACM OJ】1017: 平面切割(特别版)

    1017: 平面切割(特别版) 题目描述 我们要求的是n条闪电型折线分割平面的最大数目.比如,一条闪电型折线可以将平面分成两部分,两条最多可以将平面分成12部分,三条最多可将平面分成31部分,四条最多 ...

  4. ajax请求action返回json数据

    1,服务端: HttpServletResponse response = ServletActionContext.getResponse(); response.setContentType(&q ...

  5. XVI Open Cup named after E.V. Pankratiev. GP of Siberia

    A. Passage 枚举两个点,看看删掉之后剩下的图是否是二分图. #include <bits/stdc++.h> using namespace std ; const int MA ...

  6. Ajax工作流程

    Ajax通过XMLHttpRequest对象实现异步方式在后台发送发送请求. 主要有以下四个步骤: (1)初始化XMLHttpRequest对象.不同浏览器的差异,需要我们创建一个跨浏览器的对象,并判 ...

  7. [基础技能] 安全技术——哈希算法密码破解之彩虹表(Rainbow Table)学习

    1.基础知识 刚刚学习过数字签名的相关知识,以及数字签名的伪造技术,而伪造数字签名归根结底就是密码破解的一个过程,然而直接破解的速度是非常缓慢的,所以有人想出一种办法,直接建立出一个数据文件,里面事先 ...

  8. Curator leader 选举(一)

    要想使用Leader选举功能,需要添加recipes包,可以在maven中添加如下依赖: <dependency> <groupId>org.apache.curator< ...

  9. 我对 Java 标识符的分类命名方法

    我对 Java 的各种标识符有一套固定的分类方法,以下分享一下我的命名方法以及进行一些说明. # 前缀: 方法 方法:f_doSomeThing().分类词是 f,采自 [f]unction 方法,也 ...

  10. postman测试接口之POST提交本地文件数据

    前言: 接口测试时,有时需要读取文件的数据:那么postman怎么添加一个文件作为参数呢? 实例: 接口地址: http://121.xxx.xxx.xxx:9003/marketAccount/ba ...