我正在尝试执行XSLT转换,以将XML转换为不同的XML格式。我也在使用Xslt扩展对象在转换期间执行数据映射操作。
我在下面提到了XML格式的简化版本。有一个映射表,用于在我的系统中映射供应商产品代码和ProductID。所以在转换时我使用扩展对象来做这样的映射操作。
这很有效,直到我得到一个包含1000个详细信息节点的大型XML文档。在这种情况下,扩展对象会对性能产生影响。
我想知道是否可以将XSLT转换转换为SQL Server并处理数据库级别的数据映射操作,而不是从扩展对象进行单独的数据库调用?
源XML文档
<SuppliersDocument>
<SupplierProducts>
<Product>
<ProductCode>A001</ProductCode>
<UOM>KG</UOM>
</Product>
<Product>
<ProductCode>A002</ProductCode>
<UOM>ML</UOM>
</Product>
<Product>
<ProductCode>A003</ProductCode>
<UOM>EA</UOM>
</Product>
</SupplierProducts>
</SuppliersDocument>
目标XML文档
<MyDocument>
<Products>
<Product>
<ProductID>998998</ProductID>
<UnitOfMeasurementID>1</UnitOfMeasurementID>
</Product>
<Product>
<ProductID>885855</ProductID>
<UnitOfMeasurementID>2521</UnitOfMeasurementID>
</Product>
<Product>
<ProductID>225235</ProductID>
<UnitOfMeasurementID>5542</UnitOfMeasurementID>
</Product>
</Products>
</MyDocument>
答案 0 :(得分:0)
如果您还没有这样做,则应该在SSIS中执行此操作,而不是在SQL Server本身中执行此操作。
答案 1 :(得分:0)
public partial class StoredProcedures{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void CKTransfrom(out string resultXML, string inputXML, string transformXSL){
XslCompiledTransform proc = new XslCompiledTransform();
using (StringReader sr = new StringReader(transformXSL)){
using (XmlReader xr = XmlReader.Create(sr)){
proc.Load(xr);
}
}
using (StringReader sr = new StringReader(inputXML)){
using (XmlReader xr = XmlReader.Create(sr)){
using (StringWriter sw = new StringWriter()){
proc.Transform(xr, null, sw);
resultXML = sw.ToString();
}}}}}
在PowerShell中:
x:>"0x" +[System.BitConverter]::ToString([System.IO.File]::ReadAllBytes("X:\StoredProcedures.dll")).Replace("-","")
3)在MS-SQL
EXEC sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
EXEC sp_configure 'clr strict security', 0;
GO
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
GO
RECONFIGURE
GO
CREATE ASSEMBLY StoredProcedures
FROM 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C01030062B6DA5B0000000000000000E00022200B013000000C000000060000000000005E2A00000020000000400000000000100020000000020000040000000000000006000000000000000080000000020000000000000300608500001000001000000000100000100000000000001000000000000000000000000C2A00004F00000000400000A803000000000000000000000000000000000000006000000C000000D42800001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000640A000000200000000C000000020000000000000000000000000000200000602E72737263000000A80300000040000000040000000E0000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000001200000000000000000000000000004000004200000000000000000000000000000000402A0000000000004800000002000500402100009407000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B300400980000000100001100731000000A0A04731100000A0B0007281200000A0C0006086F1300000A0000DE0B082C07086F1400000A00DC00DE0B072C07076F1400000A00DC03731100000A0D0009281200000A130400731500000A1305000611041411056F1600000A000211056F1700000A5100DE0D11052C0811056F1400000A00DC00DE0D11042C0811046F1400000A00DC00DE0B092C07096F1400000A00DC2A01400000020016000C22000B0000000002000E002230000B0000000002005300196C000D0000000002004B00317C000D00000000020042004A8C000B000000002202281800000A002A00000042534A4201000100000000000C00000076342E302E33303331390000000005006C0000006C020000237E0000D80200006403000023537472696E6773000000003C060000040000002355530040060000100000002347554944000000500600004401000023426C6F620000000000000002000001471502000900000000FA0133001600000100000019000000020000000200000003000000180000000F0000000100000001000000030000000000070201000000000006005901FB020600C601FB0206007700C9020F001B03000006009F005C0206003C015C0206001D015C020600AD015C02060079015C02060092015C020600CC005C0206008B00DC0206006900DC02060000015C020600E700E40106003B0334020A00B600A8020E004702250206006E022A000E007B021A02060090022A00060085022A0006004E0034020E004203250206009D022A000000000001000000000001000100010010002A03000041000100010050200000000096003B024D0001003421000000008618C30206000400020001000A00000002001400000003001D000900C30201001100C30206001900C3020A002900C30210003100C30210003900C30210004100C30210004900C30210005100C30210005900C30210006100C30215006900C30210007100C30210007900C30210008900C30206009100C30206009900C3021000A10062002900910049003000B9005A000600A900C30206009100520236008100FE0140008100C302060020007B003D012E000B0055002E0013005E002E001B007D002E00230086002E002B009A002E0033009A002E003B009A002E00430086002E004B00A0002E0053009A002E005B009A002E006300B8002E006B00E2002E007300EF001A00048000000100000000000000000000000000530300000400000000000000000000004400400000000000040000000000000000000000440034000000000004000000000000000000000044001A020000000000000000003C4D6F64756C653E00726573756C74584D4C00696E707574584D4C007472616E73666F726D58534C0053797374656D2E494F0053797374656D2E44617461006D73636F726C6962004C6F61640049446973706F7361626C6500446973706F73650043726561746500477569644174747269627574650044656275676761626C6541747472696275746500436F6D56697369626C6541747472696275746500417373656D626C795469746C654174747269627574650053716C50726F63656475726541747472696275746500417373656D626C7954726164656D61726B417474726962757465005461726765744672616D65776F726B41747472696275746500417373656D626C7946696C6556657273696F6E41747472696275746500417373656D626C79436F6E66696775726174696F6E41747472696275746500417373656D626C794465736372697074696F6E41747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500417373656D626C7950726F6475637441747472696275746500417373656D626C79436F7079726967687441747472696275746500417373656D626C79436F6D70616E794174747269627574650052756E74696D65436F6D7061746962696C6974794174747269627574650053797374656D2E52756E74696D652E56657273696F6E696E6700546F537472696E6700437265617465417373656D626C792E646C6C0053797374656D2E586D6C0053797374656D2E586D6C2E58736C0053797374656D00434B5472616E7366726F6D0058736C436F6D70696C65645472616E73666F726D0053797374656D2E5265666C656374696F6E00537472696E6752656164657200586D6C526561646572005465787452656164657200537472696E675772697465720054657874577269746572004D6963726F736F66742E53716C5365727665722E536572766572002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E496E7465726F7053657276696365730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F6465730053746F72656450726F63656475726573004F626A6563740058736C74417267756D656E744C69737400437265617465417373656D626C79000000000000006B3E2490D5587141845DE3F0636C10C400042001010803200001052001011111042001010E04200101020E07061249124D1251124D1251125506000112511259052001011251092003011251126112650320000E08B77A5C561934E08907000301100E0E0E0801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F7773010801000701000000001301000E437265617465417373656D626C79000005010000000017010012436F7079726967687420C2A920203230313800002901002462333336616235662D396164362D346464332D396332652D66323237663163373762383200000C010007312E302E302E3000004D01001C2E4E45544672616D65776F726B2C56657273696F6E3D76342E362E310100540E144672616D65776F726B446973706C61794E616D65142E4E4554204672616D65776F726B20342E362E31040100000000000000000062B6DA5B00000000020000001C010000F0280000F00A000052534453009A1A205D1BDA4DA7DAB2D5EE3F4FBE01000000433A5C55736572735C63616B7530305C736F757263655C7265706F735C437265617465417373656D626C795C437265617465417373656D626C795C6F626A5C44656275675C437265617465417373656D626C792E7064620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000342A000000000000000000004E2A0000002000000000000000000000000000000000000000000000402A0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF250020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001001000000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000000000048000000584000004C03000000000000000000004C0334000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000001000000000000000100000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004AC020000010053007400720069006E006700460069006C00650049006E0066006F0000008802000001003000300030003000300034006200300000001A000100010043006F006D006D0065006E007400730000000000000022000100010043006F006D00700061006E0079004E0061006D006500000000000000000046000F000100460069006C0065004400650073006300720069007000740069006F006E000000000043007200650061007400650041007300730065006D0062006C00790000000000300008000100460069006C006500560065007200730069006F006E000000000031002E0030002E0030002E003000000046001300010049006E007400650072006E0061006C004E0061006D006500000043007200650061007400650041007300730065006D0062006C0079002E0064006C006C00000000004800120001004C006500670061006C0043006F007000790072006900670068007400000043006F0070007900720069006700680074002000A90020002000320030003100380000002A00010001004C006500670061006C00540072006100640065006D00610072006B00730000000000000000004E00130001004F0072006900670069006E0061006C00460069006C0065006E0061006D006500000043007200650061007400650041007300730065006D0062006C0079002E0064006C006C00000000003E000F000100500072006F0064007500630074004E0061006D0065000000000043007200650061007400650041007300730065006D0062006C00790000000000340008000100500072006F006400750063007400560065007200730069006F006E00000031002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000031002E0030002E0030002E00300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000603A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
WITH PERMISSION_SET = SAFE;
4)创建sp:
CREATE PROCEDURE usp_CKTransform(
@resultXML nvarchar(1000) OUTPUT,
@inputXML nvarchar(1000),
@transformXSL nvarchar(1000)
)
AS EXTERNAL NAME StoredProcedures.[StoredProcedures].CKTransfrom;
GO
5)试试
Declare @resultXML nvarchar(1000);
Declare @inputXML nvarchar(1000) = '
<catalog>
<cd>
<title>Empire Burlesque</title>
<ärtist> Böb Dylan </ärtist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<ärtist> Bonnie Tyler </ärtist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>';
Declare @transformXSL nvarchar(1000) = '
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="catalog/cd">
EXEC UPSERT_CDKektion Stevön!
''<xsl:value-of select="title"/>'',
''<xsl:value-of select="substring(ärtist,3,3)"/>'',
''<xsl:value-of select="country"/>'',
''<xsl:value-of select="company"/>'',
<xsl:value-of select="price"/>,
''<xsl:value-of select="year"/>'';
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>';
EXEC usp_CKTransform @resultXML out, @inputXML, @transformXSL;
6)享受结果:
SELECT @resultXML;
EXEC PTJ_UPSERT_CDKektion Stevön! 'Empire Burlesque', 'öb ', 'USA', 'Columbia', 10.90, '1985';
EXEC PTJ_UPSERT_CDKektion Stevön! 'Hide your heart', 'onn', 'UK', 'CBS Records', 9.90, '1988';
答案 2 :(得分:0)
要处理较大的XML-XSLT,请通过以下方法使用NVARCHAR(MAX):
using System.Data.SqlTypes;
...
public static void CKTransfrom(out SqlString resultXML, SqlString inputXML, SqlString transformXSL)
...
using (StringReader sr = new StringReader(transformXSL.Value))
...
using (StringReader sr = new StringReader(inputXML.Value))
...
和
CREATE PROCEDURE usp_CKTransform
(
@resultXML nvarchar(MAX) OUTPUT,
@inputXML nvarchar(MAX),
@transformXSL nvarchar(MAX)
)
和
0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010300E5C2E25B0000000000000000E00022200B013000000C00000006000000000000C22A0000002000000040000000000010002000000002000004000000000000000600000000000000008000000002000000000000030060850000100000100000000010000010000000000000100000000000000000000000702A00004F00000000400000A803000000000000000000000000000000000000006000000C000000382900001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000C80A000000200000000C000000020000000000000000000000000000200000602E72737263000000A80300000040000000040000000E0000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000001200000000000000000000000000004000004200000000000000000000000000000000A42A000000000000480000000200050058210000E007000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B300400AD0000000100001100731000000A0A0F02281100000A731200000A0B0007281300000A0C0006086F1400000A0000DE0B082C07086F1500000A00DC00DE0B072C07076F1500000A00DC0F01281100000A731200000A0D0009281300000A130400731600000A1305000611041411056F1700000A000211056F1800000A281900000A811200000100DE0D11052C0811056F1500000A00DC00DE0D11042C0811046F1500000A00DC00DE0B092C07096F1500000A00DC2A0000000140000002001C000C28000B00000000020014002236000B0000000002005F002281000D00000000020057003A91000D0000000002004E0053A1000B000000002202281A00000A002A00000042534A4201000100000000000C00000076342E302E33303331390000000005006C0000007C020000237E0000E80200009803000023537472696E67730000000080060000040000002355530084060000100000002347554944000000940600004C01000023426C6F620000000000000002000001471502000900000000FA013300160000010000001A0000000200000002000000030000001A0000000F00000001000000010000000300000000001B02010000000000060059010F030600C6010F0306007700DD020F002F03000006009F00700206003C01700206001D0170020600AD0170020600790170020600920170020600CC00700206008B00F00206006900F0020600000170020600E700EE010600640348020A00B600BC020A0008023E030E005B023902060082022A000E008F022E020600A4022A00060099022A0006004E0048020E00770339020600B1022A000000000001000000000001000100010010005303000041000100010050200000000096004F02530001004C21000000008618D70206000400020001000A00000002001400000003001D000900D70201001100D70206001900D7020A002900D70210003100D70210003900D70210004100D70210004900D70210005100D70210005900D70210006100D70215006900D70210007100D70210007900D70210008900D70206009900D70206009100E4012900A100D7021000A90062002D00990049003400C1005A000600B100D7020600990066023A0081001202290091006B0344008100D702060020007B0046012E000B005E002E00130067002E001B0086002E0023008F002E002B00A3002E003300A3002E003B00A3002E0043008F002E004B00A9002E005300A3002E005B00A3002E006300C1002E006B00EB002E007300F8001A00048000000100000000000000000000000000880300000400000000000000000000004A004000000000000400000000000000000000004A003400000000000400000000000000000000004A002E02000000000000003C4D6F64756C653E00726573756C74584D4C00696E707574584D4C007472616E73666F726D58534C0053797374656D2E494F0053797374656D2E44617461006D73636F726C6962004C6F61640049446973706F7361626C6500446973706F73650043726561746500477569644174747269627574650044656275676761626C6541747472696275746500436F6D56697369626C6541747472696275746500417373656D626C795469746C654174747269627574650053716C50726F63656475726541747472696275746500417373656D626C7954726164656D61726B417474726962757465005461726765744672616D65776F726B41747472696275746500417373656D626C7946696C6556657273696F6E41747472696275746500417373656D626C79436F6E66696775726174696F6E41747472696275746500417373656D626C794465736372697074696F6E41747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500417373656D626C7950726F6475637441747472696275746500417373656D626C79436F7079726967687441747472696275746500417373656D626C79436F6D70616E794174747269627574650052756E74696D65436F6D7061746962696C697479417474726962757465006765745F56616C75650053797374656D2E52756E74696D652E56657273696F6E696E670053716C537472696E6700546F537472696E6700437265617465417373656D626C792E646C6C0053797374656D2E586D6C0053797374656D2E586D6C2E58736C0053797374656D00434B5472616E7366726F6D0058736C436F6D70696C65645472616E73666F726D0053797374656D2E5265666C656374696F6E00537472696E6752656164657200586D6C526561646572005465787452656164657200537472696E675772697465720054657874577269746572004D6963726F736F66742E53716C5365727665722E536572766572002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E496E7465726F7053657276696365730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F6465730053797374656D2E446174612E53716C54797065730053746F72656450726F63656475726573004F626A656374006F705F496D706C696369740058736C74417267756D656E744C69737400437265617465417373656D626C79000000000000269396D1FF6B5041B96C473003E13AF300042001010803200001052001011111042001010E04200101020E0706124D125112551251125512590320000E0600011255125D0520010112550920030112551265126905000111490E08B77A5C561934E0890A000301101149114911490801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F7773010801000701000000001301000E437265617465417373656D626C79000005010000000017010012436F7079726967687420C2A920203230313800002901002462333336616235662D396164362D346464332D396332652D66323237663163373762383200000C010007312E302E302E3000004D01001C2E4E45544672616D65776F726B2C56657273696F6E3D76342E362E310100540E144672616D65776F726B446973706C61794E616D65142E4E4554204672616D65776F726B20342E362E3104010000000000000000E5C2E25B00000000020000001C01000054290000540B0000525344530C9334335F35814AAADE94F41F8C004801000000433A5C55736572735C63616B7530305C736F757263655C7265706F735C437265617465417373656D626C795C437265617465417373656D626C795C6F626A5C44656275675C437265617465417373656D626C792E7064620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000982A00000000000000000000B22A0000002000000000000000000000000000000000000000000000A42A0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF2500200010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001001000000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000000000048000000584000004C03000000000000000000004C0334000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000001000000000000000100000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004AC020000010053007400720069006E006700460069006C00650049006E0066006F0000008802000001003000300030003000300034006200300000001A000100010043006F006D006D0065006E007400730000000000000022000100010043006F006D00700061006E0079004E0061006D006500000000000000000046000F000100460069006C0065004400650073006300720069007000740069006F006E000000000043007200650061007400650041007300730065006D0062006C00790000000000300008000100460069006C006500560065007200730069006F006E000000000031002E0030002E0030002E003000000046001300010049006E007400650072006E0061006C004E0061006D006500000043007200650061007400650041007300730065006D0062006C0079002E0064006C006C00000000004800120001004C006500670061006C0043006F007000790072006900670068007400000043006F0070007900720069006700680074002000A90020002000320030003100380000002A00010001004C006500670061006C00540072006100640065006D00610072006B00730000000000000000004E00130001004F0072006900670069006E0061006C00460069006C0065006E0061006D006500000043007200650061007400650041007300730065006D0062006C0079002E0064006C006C00000000003E000F000100500072006F0064007500630074004E0061006D0065000000000043007200650061007400650041007300730065006D0062006C00790000000000340008000100500072006F006400750063007400560065007200730069006F006E00000031002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000031002E0030002E0030002E00300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000C43A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000