使用无界子元素在XSLT上开发的XML到CSV格式不佳

时间:2017-02-24 16:08:45

标签: xml xslt export-to-csv

Stackoverflow新手,并向XML询问有关XML的问题。我是一名具有SPSS背景的数据管理员,所以XML并不总是我的强项。我正在尝试将从分层数据库导出并以XML格式存储的数据集转换为CSV格式,原因有很多。原始数据库的结构不是很好,这导致了我的XSLT问题。

  1. 这是我必须使用的XML。这是一个700mb的文件:

      <ABC_Data>
        <UID>1</UID>
        <DocumentNumber>000000001</DocumentNumber>
        <Surname>Smith</Surname>
        <GivenName>John</GivenName>
        <BirthDateList>
            <BirthDate>19/06/19888</BirthDate>
        </BirthDateList>
        <StationNumberList>
            <StationNumber>2009981</StationNumber>
        </StationNumberList>
        <Reference>
            <ReferenceEn>RG 150, Volume 01 - 1</ReferenceEn>
            <ReferenceFr>RG 150, Volume 01 - 1</ReferenceFr>
        </Reference>
        <DigitizeList>
            <Image>http://data.foo.bar.com/733a.gif</Image>
            <Image>http://data2.for.bar.com/733b.gif</Image>
        </DigitizeList>
        <UID>2</UID>
        <DocumentNumber>000000002</DocumentNumber>
        <Surname>Kootz</Surname>
        <GivenName>Ernst</GivenName>
        <BirthDateList>
            <BirthDate>24/12/1984</BirthDate>
        </BirthDateList>
        <StationNumberList>
            <StationNumber>2000023</StationNumber>
        </StationNumberList>
        <Reference>
            <ReferenceEn>RG 150, Volume 01 - 1</ReferenceEn>
            <ReferenceFr>RG 150, Volume 01 - 1</ReferenceFr>
        </Reference>
        <DigitizeList>
            <Image>http://data.foo.bar.com/744a.gif</Image>
            <Image>http://data2.for.bar.com/755b.gif</Image>
    
        </DigitizeList>
        </ABC_Data>
    
  2. 以下是基本 XSLT我正在使用(from this thread)将其转换为CSV格式。发生的事情是记录没有正确嵌套,因此我无法获得将文件中的一条记录区分开来的输出。此外,多个<Image>字段在输出上被收集在一起而没有插入分隔符,即它们将1个字段转换为2个或3个或4个不同的字段,因为<Image>的数量可能在文本[编辑:现在解决]。

  3. 这是XSLT:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="text" encoding="iso-8859-1"/>
    
        <xsl:strip-space elements="*" />
    
        <xsl:template match="/*/child::*">
        <xsl:for-each select="child::*">
        <xsl:if test="position() != last()"><xsl:value-of select="normalize-space(.)"/>;</xsl:if>
        <xsl:if test="position() = last()"><xsl:value-of select="normalize-space(.)"/>;</xsl:if>
        </xsl:for-each>
        </xsl:template>
    
        </xsl:stylesheet>

    1. 这是我想要的输出模型。它满足了对差异化记录的需求,并区分了具有相似名称的多个“图像”字段:
    2. 1;0000000001;Smith;John;19/06/19888;2009981;RG 150, Volume 01 - 1;RG 150, Volume 01 - 1;>http://data.foo.bar.com/733a.gif;http://data2.for.bar.com/733b.gif
      2;0000000002;Koontz;Ernst;24/12/1984;2000023;RG 150, Volume 01 - 1;RG 150, Volume 01 - 1;http://data.foo.bar.com/744a.gif;http://data2.for.bar.com/755b.gif

      有人能提出前进的方向吗?我想清理它以便

      • 输出中的单独图像字段中的所有内容都在它们之间以分号显示。 [编辑:解决了,谢谢你hivemind!]
      • 我可以区分记录1和记录2,记录3等

      我的XSLT知识已有近10年的历史,所以我可以利用社区的支持来获得帮助。

      感谢。

2 个答案:

答案 0 :(得分:1)

试试这个

DECLARE @Shift TABLE (ShiftID INT, [Day] VARCHAR(3), DoctorId INT, FromTime TIME, ToTime TIME)
INSERT INTO @Shift 
VALUES (1,'SUN',1,'08:00:00','16:00:00'),
       (2,'MON',1,'09:00:00.00','14:00:00'),
       (3,'TUE',1,'09:00:00.00','15:00:00'),
       (4,'WED',1,'10:00:00.00','17:00:00'),
       (5,'THU',1,'13:00:00.00','18:00:00')

DECLARE @D1 DATE, @D2 DATE
SET @D1 = '2017-02-19'
SET @D2 = '2017-02-28'

;WITH Calendar AS
(
    SELECT @D1 AS [DateVal], LEFT(DATENAME(WEEKDAY,@D1),3) AS [DWName]
    UNION ALL
    SELECT DATEADD(DAY,1,DateVal), LEFT(DATENAME(WEEKDAY,DATEADD(DAY,1,DateVal)),3)
    FROM Calendar
    WHERE DateVal<@D2
)


SELECT S.DoctorId, C.DateVal, S.[Day], S.FromTime, S.ToTime
FROM @Shift S
JOIN Calendar C ON S.[Day]=C.DWName

答案 1 :(得分:0)

AFAICT,以下样式表将产生与预期输出几乎相同的结果:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*" />

<xsl:key name="cells" match="ABC_Data/*[not(self::UID)]" use="generate-id(preceding-sibling::UID[1])" />

<xsl:template match="/ABC_Data">
    <xsl:for-each select="UID">
        <xsl:apply-templates select=". | key('cells', generate-id())"/>
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

<xsl:template match="*[not(*)]">
    <xsl:value-of select="." />
    <xsl:text>;</xsl:text>
</xsl:template>

</xsl:stylesheet>

唯一的区别是每行保留一个尾随的;字符。这是因为我们不知道哪个元素是其行中的最后一个单元格 - 以及它是否包含多个子元素。

如果你知道这一点,你可以添加一个名称匹配的模板。否则,您必须先将每行放入变量中,然后输出变量而不使用其最后一个字符:

<xsl:template match="/ABC_Data">
    <xsl:for-each select="UID">
        <xsl:variable name="row">
            <xsl:apply-templates select=". | key('cells', generate-id())"/>
        </xsl:variable>
        <xsl:value-of select="substring($row, 1, string-length($row) - 1)" />
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

顺便说一句,我怀疑这个结果是否有用。作为CSV文件的收件人,我希望每列都有来自同一域的数据(事实上,我希望每列都有一个标签)。至少在理论上,您的输入可能包含具有不同数量的BirthDates,StationNumbers,References等的记录,从而导致行包含未对齐列中的不同数量的单元格。