XSLT 1.0对XML进行排序和分组

时间:2016-01-08 03:54:34

标签: xml sorting xslt xslt-1.0 grouping

我需要一个转换以下XML的XSLT 1.0。

<Record>
  <Row>
    <Name>AAA</Name>
    <Surname>Surname1</Surname>
  </Row>
  <Row>
    <Name>BBB</Name>
    <Surname>Surname2</Surname>
  </Row>
  <Row>
    <Name>CCC</Name>
    <Surname>Surname1</Surname>
  </Row>
  <Row>
    <Name>DDD</Name>
    <Surname>Surname2</Surname>
  </Row>
  <Row>
    <Name>EEE</Name>
    <Surname>Surname1</Surname>
  </Row>
  <Row>
    <Name>FFF</Name>
    <Surname>Surname2</Surname>
  </Row>
  <Row>
    <Name>GGG</Name>
    <Surname>Surname1</Surname>
  </Row>
  <Row>
    <Name>HHH</Name>
    <Surname>Surname2</Surname>
  </Row>
</Record>

我期待的输出是:

<Output>
  <Recordset1>
    <Record>
      <Name>AAA</Name>
      <Surname>Surname1</Surname>
    </Record>
    <Record>
      <Name>CCC</Name>
      <Surname>Surname1</Surname>
    </Record>
  </Recordset1>
  <Recordset1>
    <Record>
      <Name>EEE</Name>
      <Surname>Surname1</Surname>
    </Record>
    <Record>
      <Name>GGG</Name>
      <Surname>Surname1</Surname>
    </Record>
  </Recordset1>
  <Recordset2>
    <Record>
      <Name>BBB</Name>
      <Surname>Surname2</Surname>
    </Record>
    <Record>
      <Name>DDD</Name>
      <Surname>Surname2</Surname>
    </Record>
  </Recordset2>
  <Recordset2>
    <Record>
      <Name>FFF</Name>
      <Surname>Surname2</Surname>
    </Record>
    <Record>
      <Name>HHH</Name>
      <Surname>Surname2</Surname>
    </Record>
  </Recordset2>
</Output>

条件是:

  1. Recordset1应包含Surname1节点

  2. Recordset2应包含Surname2节点

  3. 输出应按姓氏

  4. 排序
  5. 每个记录集最多有2个记录。

2 个答案:

答案 0 :(得分:0)

这可能会对你有帮助。

<script>

    var margin = {top: 8, right: 10, bottom: 2, left: 10},
            width = 1160 - margin.left - margin.right,
            height = 100 - margin.top - margin.bottom;

    var parseDate = d3.timeParse("%H:%M:%S");

    var x = d3.scaleTime()
            .range([0, width]);

    var y = d3.scaleLinear()
            .range([height, 0]);

    var area = d3.area()
            .x(function (d) {
                return x(d.time);
            })
            .y0(height)
            .y1(function (d) {
                return y(d.stress);
            });

    var line = d3.line()
            .x(function (d) {
                return x(d.time);
            })
            .y(function (d) {
                return y(d.stress);
            });

    d3.csv("6000smokedData3.csv", type, function (error, data) {

        // Nest data by date.
        var dates = d3.nest()
                .key(function (d) {
                    return d.date;
                })
                .entries(data);

        // Compute the maximum stress per date, needed for the y-domain.
        dates.forEach(function (s) {
            s.maxPrice = d3.max(s.values, function (d) {
                return d.stress;
            });
        });

        // Compute the minimum and maximum time across dates.
        // We assume values are sorted by time.
        x.domain([
            d3.min(dates, function (s) {
                return s.values[0].time;
            }),
            d3.max(dates, function (s) {
                return s.values[s.values.length - 1].time;
            })
        ]);

        // Add an SVG element for each date, with the desired dimensions and margin.
        var svg = d3.select("body").selectAll("svg")
                .data(dates)
                .enter().append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        //Add the scatterplot
        svg.selectAll("dot")
                .data(data)
                .enter().append("circle")
                .attr("r", 4)
                .style("fill", function (d) {
                    return "red";
                })
                .attr("cx", function (d) {
                    if (d.smoked == 1) {
                        return x(d.time);
                    }
                })
                .attr("cy", function (d) {
                    if (d.smoked == 1) {
                        return y(d.stress);
                    }
                });

        // Add the X Axis
        svg.append("g")
                .attr("transform", "translate(0," + height + ")")
                .call(d3.axisBottom(x));

        // Add the Y Axis
        svg.append("g")
                .call(d3.axisLeft(y));

        // Add the area path elements. Note: the y-domain is set per element.
        svg.append("path")
                .attr("class", "area")
                .attr("d", function (d) {
                    y.domain([0, d.maxPrice]);
                    return area(d.values);
                });

        // Add the line path elements. Note: the y-domain is set per element.
        svg.append("path")
                .attr("class", "line")
                .attr("d", function (d) {
                    y.domain([0, d.maxPrice]);
                    return line(d.values);
                });

        // Add a small label for the date name.
        svg.append("text")
                .attr("x", width - 6)
                .attr("y", height - 6)
                .style("text-anchor", "end")
                .text(function (d) {
                    return d.key;
                });
    });

    function type(d) {
        d.stress = +d.stress;
        d.time = parseDate(d.time);
        d.smoked = +d.smoked;
        return d;
    }

</script>

答案 1 :(得分:0)

有一些技巧:

  1. 选择不同的姓氏。
  2. 对姓氏记录进行分组。
  3. 将姓氏记录分为2组。
  4. 根据不同的姓氏索引创建动态元素名称。
  5. 此样式表解决了这些问题:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
      <xsl:template match="/Record">
        <Output>
          <xsl:apply-templates select="//Row[not(./Surname=preceding::Surname)]/Surname" mode="group">
            <xsl:sort select="Surname" />
          </xsl:apply-templates>
        </Output>
      </xsl:template>
    
      <xsl:template match="Surname" mode="group">
        <xsl:variable name="surname" select="./text()" />
        <xsl:variable name="surnameIndex" select="position()" />
        <xsl:variable name="surnameGroup" select="/Record/Row[Surname/text()=$surname]" />
        <xsl:for-each select="$surnameGroup">
          <xsl:variable name="index" select="position()" />
          <!-- only create a new record set on the first (or only) member of the output pair -->
          <xsl:if test="$index mod 2=1"> <!-- positions are 1 based, not 0 based -->
            <xsl:element name="Recordset{$surnameIndex}">
              <!-- alternatively, use xsl:copy-of -->
              <xsl:apply-templates select="$surnameGroup[$index]" mode="record" />
              <xsl:apply-templates select="$surnameGroup[$index+1]" mode="record" />
            </xsl:element>
          </xsl:if>
        </xsl:for-each>
      </xsl:template>
    
      <xsl:template match="Row" mode="record">
        <Record>
          <Name><xsl:value-of select="Name" /></Name>
          <Surname><xsl:value-of select="Surname" /></Surname>
        </Record>
      </xsl:template>
    
    </xsl:stylesheet>
    

    对输入文档应用此样式表的输出是:

    <?xml version="1.0" encoding="UTF-8" ?>
    <Output>
      <Recordset1>
        <Record>
          <Name>AAA</Name>
          <Surname>Surname1</Surname>
        </Record>
        <Record>
          <Name>CCC</Name>
          <Surname>Surname1</Surname>
        </Record>
      </Recordset1>
      <Recordset1>
        <Record>
          <Name>EEE</Name>
          <Surname>Surname1</Surname>
        </Record>
        <Record>
          <Name>GGG</Name>
          <Surname>Surname1</Surname>
        </Record>
      </Recordset1>
      <Recordset2>
        <Record>
          <Name>BBB</Name>
          <Surname>Surname2</Surname>
        </Record>
        <Record>
          <Name>DDD</Name>
          <Surname>Surname2</Surname>
        </Record>
      </Recordset2>
      <Recordset2>
        <Record>
          <Name>FFF</Name>
          <Surname>Surname2</Surname>
        </Record>
        <Record>
          <Name>HHH</Name>
          <Surname>Surname2</Surname>
        </Record>
      </Recordset2>
    </Output>