XSLT 1.0 输出十六进制 0x1C - 0x1F 到文本文件
XSLT 1.0 Output Hex 0x1C - 0x1F to Text File
我正在使用 xslt 1.0 版将 XML 文件转换为发送给第三方的文本文件。第三方格式要求数据字段用0x1F(ascii单位分隔符)分隔,组用0x1D(ascii组分隔符)分隔,记录用0x1E(ascii记录分隔符)分隔。在样式中使用这些 sheet 会导致以下错误。
Character ' ', hexadecimal value 0x1D is illegal in XML documents.
我目前正在使用扩展字符集中的 0x80 到 0x82,然后 运行 通过 c# 中的替换函数的转换结果将我使用的值替换为我实际需要的值,但看起来应该有更好、更有效的方法来做到这一点。
有没有办法直接使用 sheet 样式将这些值输出到文本文件?
当前样式sheet
<?xml version="1.0" encoding="us-ascii"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:asap="http://www.asapnet.org/pmp/4.2/exchange"
xmlns:asap-code="http://www.asapnet.org/pmp/4.2/extension/code"
xmlns:asap-ext="http://www.asapnet.org/pmp/4.2/extension"
xmlns:asap-meta="http://www.asapnet.org/pmp/4.2/extension/meta"
xmlns:nc="http://release.niem.gov/niem/niem-core/3.0/"
exclude-result-prefixes="asap asap-code asap-ext asap-meta nc">
<xsl:output method="text" omit-xml-declaration="yes" indent="no" />
<xsl:variable name="FieldSeparator" select="''"/>
<xsl:variable name="SegmentTerminator" select="'€'"/>
<!--MAIN-->
<xsl:template match="asap:ReportTransmission">
<xsl:apply-templates select="asap-meta:TransactionHeader"/>
<xsl:apply-templates select="asap-meta:InformationSource"/>
<xsl:apply-templates select="asap-ext:ReportingPharmacy"/>
</xsl:template>
<!--TRANSACTION HEADER - TH SEGMENT-->
<xsl:template match="asap-meta:TransactionHeader">
<xsl:value-of select="concat(
'TH',
$FieldSeparator,
asap-meta:ReleaseNumberText,
$FieldSeparator,
asap-meta:ControlNumberText,
$FieldSeparator,
asap-code:TransactionKindCode,
$FieldSeparator,
concat(substring(asap-meta:TransactionDate,1,4),substring(asap-meta:TransactionDate,6,2),substring(asap-meta:TransactionDate,9,2)),
$FieldSeparator,
concat(substring(asap-meta:TransactionTime,1,2),substring(asap-meta:TransactionTime,4,2)),
$FieldSeparator,
asap-code:FileKindCode,
$FieldSeparator,
asap-meta:RoutingNumber,
$FieldSeparator,
$SegmentTerminator,
$SegmentTerminator)" />
</xsl:template>
<!--INFORMATION SOURCE - IS SEGMENT-->
<xsl:template match="asap-meta:InformationSource">
<xsl:value-of select="concat(
'IS',
$FieldSeparator,
nc:Identification/nc:IdentificationID,
$FieldSeparator,
nc:Identification/nc:IdentificationJurisdiction/nc:JurisdictionText,
$FieldSeparator,
nc:MessageText,
$SegmentTerminator)" />
</xsl:template>
</xsl:stylesheet>
(... 样式 sheet 继续附加段 ... )
当前输出 (Notepad++)
(...输出继续附加段...)
XML样本
<?xml version="1.0" encoding="UTF-8"?>
<asap:ReportTransmission xmlns:asap="http://www.asapnet.org/pmp/4.2/exchange"
xmlns:asap-code="http://www.asapnet.org/pmp/4.2/extension/code"
xmlns:asap-ext="http://www.asapnet.org/pmp/4.2/extension"
xmlns:asap-meta="http://www.asapnet.org/pmp/4.2/extension/meta"
xmlns:nc="http://release.niem.gov/niem/niem-core/3.0/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.asapnet.org/pmp/4.2/exchange ../schemas/exchange/pmp_exchange.xsd">
<asap-meta:TransactionHeader>
<asap-meta:ReleaseNumberText>4.2</asap-meta:ReleaseNumberText>
<asap-meta:ControlNumberText>857463</asap-meta:ControlNumberText>
<asap-code:TransactionKindCode>01</asap-code:TransactionKindCode>
<asap-meta:TransactionDate>2009-10-15</asap-meta:TransactionDate>
<asap-meta:TransactionTime>10:45:00</asap-meta:TransactionTime>
<asap-code:FileKindCode>P</asap-code:FileKindCode>
</asap-meta:TransactionHeader>
<asap-meta:InformationSource>
<nc:Identification>
<nc:IdentificationID>7564</nc:IdentificationID>
<nc:IdentificationJurisdiction>
<nc:JurisdictionText>ACME PHARMACY</nc:JurisdictionText>
</nc:IdentificationJurisdiction>
</nc:Identification>
</asap-meta:InformationSource>
<asap-ext:ReportingPharmacy>
<asap-ext:NPIIdentification>
<nc:IdentificationID>1234567890</nc:IdentificationID>
</asap-ext:NPIIdentification>
<asap-ext:PatientInfo>
<nc:PersonBirthDate>
<nc:Date>1950-01-01</nc:Date>
</nc:PersonBirthDate>
<nc:PersonName>
<nc:PersonGivenName>John</nc:PersonGivenName>
<nc:PersonSurName>Smith</nc:PersonSurName>
</nc:PersonName>
<nc:PersonSexText>Male</nc:PersonSexText>
<asap-ext:PrimaryIdentification>
<nc:PersonLicenseIdentification>
<nc:IdentificationID>987544</nc:IdentificationID>
<nc:IdentificationJurisdiction>
<nc:LocationStateUSPostalServiceCode>MA</nc:LocationStateUSPostalServiceCode>
</nc:IdentificationJurisdiction>
</nc:PersonLicenseIdentification>
</asap-ext:PrimaryIdentification>
<nc:ContactMailingAddress>
<nc:LocationStreet>
<nc:StreetName>1234 Main St</nc:StreetName>
</nc:LocationStreet>
<nc:LocationCityName>Somewhere</nc:LocationCityName>
<nc:LocationStateUSPostalServiceCode>MA</nc:LocationStateUSPostalServiceCode>
<nc:LocationPostalCode>54356</nc:LocationPostalCode>
</nc:ContactMailingAddress>
<asap-ext:DispensingRecord>
<asap-code:ReportingStatusCode>00</asap-code:ReportingStatusCode>
<asap-ext:Prescription>
<asap-ext:PrescriptionNumberText>6542984</asap-ext:PrescriptionNumberText>
<asap-ext:PrescriptionWrittenDate>
<nc:Date>2009-10-15</nc:Date>
</asap-ext:PrescriptionWrittenDate>
<asap-ext:PrescriptionRefillQuantity>0</asap-ext:PrescriptionRefillQuantity>
<asap-ext:ProductIdentification>
<nc:IdentificationID>57866707401</nc:IdentificationID>
<asap-code:ProductIdentifierKindCode>01</asap-code:ProductIdentifierKindCode>
</asap-ext:ProductIdentification>
<asap-ext:PrescriptionSupplyQuantity>15</asap-ext:PrescriptionSupplyQuantity>
</asap-ext:Prescription>
<asap-ext:Transaction>
<asap-ext:PrescriptionFilledDate>
<nc:Date>2009-10-15</nc:Date>
</asap-ext:PrescriptionFilledDate>
<asap-ext:PrescriptionRefillNumber>0</asap-ext:PrescriptionRefillNumber>
<asap-ext:PrescriptionDispensedQuantity>30</asap-ext:PrescriptionDispensedQuantity>
</asap-ext:Transaction>
<asap-ext:Prescriber>
<asap-ext:DEAIdentification>
<nc:IdentificationID>AW8765432</nc:IdentificationID>
</asap-ext:DEAIdentification>
</asap-ext:Prescriber>
<asap-ext:AdditionalInformation>
<asap-ext:IssuingPrescriptionBlankIdentification>
<nc:IdentificationID>787456493993</nc:IdentificationID>
<nc:IdentificationJurisdiction>
<nc:LocationStateUSPostalServiceCode>MA</nc:LocationStateUSPostalServiceCode>
</nc:IdentificationJurisdiction>
</asap-ext:IssuingPrescriptionBlankIdentification>
</asap-ext:AdditionalInformation>
</asap-ext:DispensingRecord>
</asap-ext:PatientInfo>
</asap-ext:ReportingPharmacy>
</asap:ReportTransmission>
更新
对于那些可能正在寻找类似解决方案的人,我最终选择了风格为 sheet 的 C# 脚本。
<msxsl:script implements-prefix="CSharpScripts" language="C#">
public string FS()
{
return '\u001F'.ToString();
}
public string GS()
{
return '\u001D'.ToString();
}
</msxsl:script>
然后可以这样使用:
<xsl:value-of select="CSharpScripts:FS()"/>
加载 XslCompiledTransform 时,您确实需要使用 XsltSettings 设置 EnableScript = true,并在用于输出的 XmlWriter 上设置 CheckCharacters = false:
var xslt = new XslCompiledTransform();
xslt.Load(
@"E:\TFS\Transforms\TestTransform.xslt",
new XsltSettings() {EnableScript = true}, null);
var writerSettings = xslt.OutputSettings.Clone();
writerSettings.CheckCharacters = false;
var sb = new StringBuilder();
var xmlOutput = XmlWriter.Create(sb, writerSettings);
xslt.Transform(@"E:\samples.xml", xmlOutput);
感谢@Abel 为我指明了正确的方向。
您似乎是少数对使用 XML 1.1 有合理要求的人之一。事实上,正如您已经发现的那样,在 XML 1.0 中不可能使用低于 0x20 的控制字符,制表符、cr 和 lf 除外。由于 XSLT 是用 XML 编写的,这意味着您不需要可以从 XML 1.1.
读取 XSLT 实例文档的处理器
据我所知,只有一个 XSLT 1.0 处理器能够处理 XML 1.1,那就是 Saxon 6.5(或更高版本的 Saxon,但您也可以跳到使用 XSLT 2.0 或 3.0)。 Saxon 的 .NET 的 IKVM 端口存在并受支持(不,我 不 附属,事实上,我写了 Exselt,但我们还没有计划支持 XML 1.1).
您不需要将您的输入更改为 XML 1.1,只需更改您的样式表,因为那是您需要使用这些字符的地方。
在能够处理 XML 1.1 的适当 XML 编辑器中,更改以下内容:
<?xml version="1.0" encoding="UTF-8"?>
进入
<?xml version="1.1" encoding="UTF-8"?>
然后更改分隔符以使用您希望它们使用的字符:
<xsl:variable name="FieldSeparator" select="''" />
<xsl:variable name="SegmentTerminator" select="''" />
然后错误应该消失了(如果你仍然有错误,你没有使用能够处理 XML 1.1 的处理器,即在 .NET 中,你被 XML 1.0,微软没有升级的计划,因为 XML 1.1 的 "use in the wild" 非常非常小)。
其他选择是:
- 使用可以写入编码字符的扩展函数。在 .NET 中,这很简单,但是,我不知道返回 ASCII 控制字符是否会被 XML 编写器接受。
- 使用新的 EXPath binary module,但它很新,我不确定操作支持的级别是多少。但是,它适用于任何 XML 或 XSLT 版本
- Post-处理您的输出(就像您现在所做的那样)。最好使用 Unicode Private Use character,因为碰撞的可能性几乎为零。
- (您可能想在 XSLT 2.0 中使用
xsl:character-maps
或 codepoints-to-string()
,但您会 运行 遇到同样的问题,只是在稍后阶段。)
PS:设置omit-xml-declaration="yes"
和indent="no"
是多余的,文本输出永远不会有xml声明,也不会提供自动缩进。
PPS:您提供的示例 XSLT 在不符合您的描述的地方转储了大量文本。添加一个 shallow-skip 模板可以解决它,但只输出一行。我没有检查这是否符合预期。
我正在使用 xslt 1.0 版将 XML 文件转换为发送给第三方的文本文件。第三方格式要求数据字段用0x1F(ascii单位分隔符)分隔,组用0x1D(ascii组分隔符)分隔,记录用0x1E(ascii记录分隔符)分隔。在样式中使用这些 sheet 会导致以下错误。
Character ' ', hexadecimal value 0x1D is illegal in XML documents.
我目前正在使用扩展字符集中的 0x80 到 0x82,然后 运行 通过 c# 中的替换函数的转换结果将我使用的值替换为我实际需要的值,但看起来应该有更好、更有效的方法来做到这一点。
有没有办法直接使用 sheet 样式将这些值输出到文本文件?
当前样式sheet
<?xml version="1.0" encoding="us-ascii"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:asap="http://www.asapnet.org/pmp/4.2/exchange"
xmlns:asap-code="http://www.asapnet.org/pmp/4.2/extension/code"
xmlns:asap-ext="http://www.asapnet.org/pmp/4.2/extension"
xmlns:asap-meta="http://www.asapnet.org/pmp/4.2/extension/meta"
xmlns:nc="http://release.niem.gov/niem/niem-core/3.0/"
exclude-result-prefixes="asap asap-code asap-ext asap-meta nc">
<xsl:output method="text" omit-xml-declaration="yes" indent="no" />
<xsl:variable name="FieldSeparator" select="''"/>
<xsl:variable name="SegmentTerminator" select="'€'"/>
<!--MAIN-->
<xsl:template match="asap:ReportTransmission">
<xsl:apply-templates select="asap-meta:TransactionHeader"/>
<xsl:apply-templates select="asap-meta:InformationSource"/>
<xsl:apply-templates select="asap-ext:ReportingPharmacy"/>
</xsl:template>
<!--TRANSACTION HEADER - TH SEGMENT-->
<xsl:template match="asap-meta:TransactionHeader">
<xsl:value-of select="concat(
'TH',
$FieldSeparator,
asap-meta:ReleaseNumberText,
$FieldSeparator,
asap-meta:ControlNumberText,
$FieldSeparator,
asap-code:TransactionKindCode,
$FieldSeparator,
concat(substring(asap-meta:TransactionDate,1,4),substring(asap-meta:TransactionDate,6,2),substring(asap-meta:TransactionDate,9,2)),
$FieldSeparator,
concat(substring(asap-meta:TransactionTime,1,2),substring(asap-meta:TransactionTime,4,2)),
$FieldSeparator,
asap-code:FileKindCode,
$FieldSeparator,
asap-meta:RoutingNumber,
$FieldSeparator,
$SegmentTerminator,
$SegmentTerminator)" />
</xsl:template>
<!--INFORMATION SOURCE - IS SEGMENT-->
<xsl:template match="asap-meta:InformationSource">
<xsl:value-of select="concat(
'IS',
$FieldSeparator,
nc:Identification/nc:IdentificationID,
$FieldSeparator,
nc:Identification/nc:IdentificationJurisdiction/nc:JurisdictionText,
$FieldSeparator,
nc:MessageText,
$SegmentTerminator)" />
</xsl:template>
</xsl:stylesheet>
(... 样式 sheet 继续附加段 ... )
当前输出 (Notepad++)
(...输出继续附加段...)
XML样本
<?xml version="1.0" encoding="UTF-8"?>
<asap:ReportTransmission xmlns:asap="http://www.asapnet.org/pmp/4.2/exchange"
xmlns:asap-code="http://www.asapnet.org/pmp/4.2/extension/code"
xmlns:asap-ext="http://www.asapnet.org/pmp/4.2/extension"
xmlns:asap-meta="http://www.asapnet.org/pmp/4.2/extension/meta"
xmlns:nc="http://release.niem.gov/niem/niem-core/3.0/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.asapnet.org/pmp/4.2/exchange ../schemas/exchange/pmp_exchange.xsd">
<asap-meta:TransactionHeader>
<asap-meta:ReleaseNumberText>4.2</asap-meta:ReleaseNumberText>
<asap-meta:ControlNumberText>857463</asap-meta:ControlNumberText>
<asap-code:TransactionKindCode>01</asap-code:TransactionKindCode>
<asap-meta:TransactionDate>2009-10-15</asap-meta:TransactionDate>
<asap-meta:TransactionTime>10:45:00</asap-meta:TransactionTime>
<asap-code:FileKindCode>P</asap-code:FileKindCode>
</asap-meta:TransactionHeader>
<asap-meta:InformationSource>
<nc:Identification>
<nc:IdentificationID>7564</nc:IdentificationID>
<nc:IdentificationJurisdiction>
<nc:JurisdictionText>ACME PHARMACY</nc:JurisdictionText>
</nc:IdentificationJurisdiction>
</nc:Identification>
</asap-meta:InformationSource>
<asap-ext:ReportingPharmacy>
<asap-ext:NPIIdentification>
<nc:IdentificationID>1234567890</nc:IdentificationID>
</asap-ext:NPIIdentification>
<asap-ext:PatientInfo>
<nc:PersonBirthDate>
<nc:Date>1950-01-01</nc:Date>
</nc:PersonBirthDate>
<nc:PersonName>
<nc:PersonGivenName>John</nc:PersonGivenName>
<nc:PersonSurName>Smith</nc:PersonSurName>
</nc:PersonName>
<nc:PersonSexText>Male</nc:PersonSexText>
<asap-ext:PrimaryIdentification>
<nc:PersonLicenseIdentification>
<nc:IdentificationID>987544</nc:IdentificationID>
<nc:IdentificationJurisdiction>
<nc:LocationStateUSPostalServiceCode>MA</nc:LocationStateUSPostalServiceCode>
</nc:IdentificationJurisdiction>
</nc:PersonLicenseIdentification>
</asap-ext:PrimaryIdentification>
<nc:ContactMailingAddress>
<nc:LocationStreet>
<nc:StreetName>1234 Main St</nc:StreetName>
</nc:LocationStreet>
<nc:LocationCityName>Somewhere</nc:LocationCityName>
<nc:LocationStateUSPostalServiceCode>MA</nc:LocationStateUSPostalServiceCode>
<nc:LocationPostalCode>54356</nc:LocationPostalCode>
</nc:ContactMailingAddress>
<asap-ext:DispensingRecord>
<asap-code:ReportingStatusCode>00</asap-code:ReportingStatusCode>
<asap-ext:Prescription>
<asap-ext:PrescriptionNumberText>6542984</asap-ext:PrescriptionNumberText>
<asap-ext:PrescriptionWrittenDate>
<nc:Date>2009-10-15</nc:Date>
</asap-ext:PrescriptionWrittenDate>
<asap-ext:PrescriptionRefillQuantity>0</asap-ext:PrescriptionRefillQuantity>
<asap-ext:ProductIdentification>
<nc:IdentificationID>57866707401</nc:IdentificationID>
<asap-code:ProductIdentifierKindCode>01</asap-code:ProductIdentifierKindCode>
</asap-ext:ProductIdentification>
<asap-ext:PrescriptionSupplyQuantity>15</asap-ext:PrescriptionSupplyQuantity>
</asap-ext:Prescription>
<asap-ext:Transaction>
<asap-ext:PrescriptionFilledDate>
<nc:Date>2009-10-15</nc:Date>
</asap-ext:PrescriptionFilledDate>
<asap-ext:PrescriptionRefillNumber>0</asap-ext:PrescriptionRefillNumber>
<asap-ext:PrescriptionDispensedQuantity>30</asap-ext:PrescriptionDispensedQuantity>
</asap-ext:Transaction>
<asap-ext:Prescriber>
<asap-ext:DEAIdentification>
<nc:IdentificationID>AW8765432</nc:IdentificationID>
</asap-ext:DEAIdentification>
</asap-ext:Prescriber>
<asap-ext:AdditionalInformation>
<asap-ext:IssuingPrescriptionBlankIdentification>
<nc:IdentificationID>787456493993</nc:IdentificationID>
<nc:IdentificationJurisdiction>
<nc:LocationStateUSPostalServiceCode>MA</nc:LocationStateUSPostalServiceCode>
</nc:IdentificationJurisdiction>
</asap-ext:IssuingPrescriptionBlankIdentification>
</asap-ext:AdditionalInformation>
</asap-ext:DispensingRecord>
</asap-ext:PatientInfo>
</asap-ext:ReportingPharmacy>
</asap:ReportTransmission>
更新
对于那些可能正在寻找类似解决方案的人,我最终选择了风格为 sheet 的 C# 脚本。
<msxsl:script implements-prefix="CSharpScripts" language="C#">
public string FS()
{
return '\u001F'.ToString();
}
public string GS()
{
return '\u001D'.ToString();
}
</msxsl:script>
然后可以这样使用:
<xsl:value-of select="CSharpScripts:FS()"/>
加载 XslCompiledTransform 时,您确实需要使用 XsltSettings 设置 EnableScript = true,并在用于输出的 XmlWriter 上设置 CheckCharacters = false:
var xslt = new XslCompiledTransform();
xslt.Load(
@"E:\TFS\Transforms\TestTransform.xslt",
new XsltSettings() {EnableScript = true}, null);
var writerSettings = xslt.OutputSettings.Clone();
writerSettings.CheckCharacters = false;
var sb = new StringBuilder();
var xmlOutput = XmlWriter.Create(sb, writerSettings);
xslt.Transform(@"E:\samples.xml", xmlOutput);
感谢@Abel 为我指明了正确的方向。
您似乎是少数对使用 XML 1.1 有合理要求的人之一。事实上,正如您已经发现的那样,在 XML 1.0 中不可能使用低于 0x20 的控制字符,制表符、cr 和 lf 除外。由于 XSLT 是用 XML 编写的,这意味着您不需要可以从 XML 1.1.
读取 XSLT 实例文档的处理器据我所知,只有一个 XSLT 1.0 处理器能够处理 XML 1.1,那就是 Saxon 6.5(或更高版本的 Saxon,但您也可以跳到使用 XSLT 2.0 或 3.0)。 Saxon 的 .NET 的 IKVM 端口存在并受支持(不,我 不 附属,事实上,我写了 Exselt,但我们还没有计划支持 XML 1.1).
您不需要将您的输入更改为 XML 1.1,只需更改您的样式表,因为那是您需要使用这些字符的地方。
在能够处理 XML 1.1 的适当 XML 编辑器中,更改以下内容:
<?xml version="1.0" encoding="UTF-8"?>
进入
<?xml version="1.1" encoding="UTF-8"?>
然后更改分隔符以使用您希望它们使用的字符:
<xsl:variable name="FieldSeparator" select="''" />
<xsl:variable name="SegmentTerminator" select="''" />
然后错误应该消失了(如果你仍然有错误,你没有使用能够处理 XML 1.1 的处理器,即在 .NET 中,你被 XML 1.0,微软没有升级的计划,因为 XML 1.1 的 "use in the wild" 非常非常小)。
其他选择是:
- 使用可以写入编码字符的扩展函数。在 .NET 中,这很简单,但是,我不知道返回 ASCII 控制字符是否会被 XML 编写器接受。
- 使用新的 EXPath binary module,但它很新,我不确定操作支持的级别是多少。但是,它适用于任何 XML 或 XSLT 版本
- Post-处理您的输出(就像您现在所做的那样)。最好使用 Unicode Private Use character,因为碰撞的可能性几乎为零。
- (您可能想在 XSLT 2.0 中使用
xsl:character-maps
或codepoints-to-string()
,但您会 运行 遇到同样的问题,只是在稍后阶段。)
PS:设置omit-xml-declaration="yes"
和indent="no"
是多余的,文本输出永远不会有xml声明,也不会提供自动缩进。
PPS:您提供的示例 XSLT 在不符合您的描述的地方转储了大量文本。添加一个 shallow-skip 模板可以解决它,但只输出一行。我没有检查这是否符合预期。