Jasper Reports 使用 comparatorExpression 进行交叉表排序

发布于 2024-08-23 06:47:58 字数 196 浏览 7 评论 0 原文

我正在尝试根据某些自定义方案对交叉表中的动态列进行排序。

在文档中,我发现提到了comparatorExpression:交叉表组存储桶比较器表达式。这个表达式的结果是 用于按升序或降序对存储桶进行排序。如果没有比较器 指定了表达式,将使用自然顺序。

但我不明白表达式应该是什么样子。我可以使用常规的 java 比较器吗?有人可以分享一个例子吗?

I'm trying to sort my dynamic columns in a cross tab according to some custom scheme.

In the docs I found mention of comparatorExpression: Crosstab group bucket comparator expression. The result of this expression is
used to sort the buckets, in ascending or descending order. If no comparator
expression is specified, the natural order will be used.

but I don't understand what the expression should look like. Can I somehow use a regular java comparator? Can someone share an example?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

幽梦紫曦~ 2024-08-30 06:47:58

我遇到了同样的问题,没有找到任何示例或解释,但我找到了如何做到这一点。我会解释一下,希望其他人可以使用这个解决方案。

我已将下面的代码用于 jasperreports 3.1.0 和 iReport 3.1.4,但我想它几乎适用于所有版本。

首先,您必须确保您知道行/列组的存储桶表达式中的类。默认情况下,这是 java.lang.String,但我在那里有一个自定义类。为此,我需要为我的列组编辑 xml,如下所示:

<bucketExpression class="java.lang.String"><![CDATA[$F{customObj}]]></bucketExpression>

显然

<bucketExpression class="com.project.CustomObj"><![CDATA[$F{customObj}]]></bucketExpression>

,此 customObj 值是一个具有相应类的字段,在报表本身中定义。

然后您需要添加一个 Comparator 作为参数,例如:

parameters.put("OVERRIDE_Comparator", new Comparator<CustomObj>() {
    public int compare(CustomObj c1, CustomObj c2) {
        //create your custom compare logic over here, this code works as if no custom Comparator is used
        return c1.compareTo(c2);
    }
});

现在在 jasperreport 中添加这样一个 OVERRIDE_Comparator 参数,使用 java.util.Comparator 参数类。

最后一步:将 $P{OVERRIDE_Comparator} 作为比较器表达式放入您需要的行/列组中。

编译此类报告时,最可能的编译错误是转换问题。 Jasperreports 默认为 java.lang.String,您可能需要手动编辑报告的 xml 以在每一步获取正确的类。

(我从一些亚洲网站找到了这个方法,幸运的是代码本身是可读的!:-))

I've had the same issue and did not find any examples or explanation but I found how to do it. I'll explain, so hopefully other people can use this solution.

I've used the code below for jasperreports 3.1.0 and iReport 3.1.4, but I guess it works for pretty much all versions.

First you'll have to make sure that you know wich class you have in the bucket expression for your row/column group. By default this is java.lang.String, but I have a custom class there. For this to work I needed to edit the xml like this for my column group:

<bucketExpression class="java.lang.String"><![CDATA[$F{customObj}]]></bucketExpression>

to

<bucketExpression class="com.project.CustomObj"><![CDATA[$F{customObj}]]></bucketExpression>

Obviously this customObj value is a field with the corresponding class, defined in the report itself.

Then you'll need to add a Comparator as a parameter, for example:

parameters.put("OVERRIDE_Comparator", new Comparator<CustomObj>() {
    public int compare(CustomObj c1, CustomObj c2) {
        //create your custom compare logic over here, this code works as if no custom Comparator is used
        return c1.compareTo(c2);
    }
});

Now add such a OVERRIDE_Comparator parameter in the jasperreport, using the java.util.Comparator Parameter Class.

Final step: Put $P{OVERRIDE_Comparator} as the Comparator Expression in the row/column group you needed.

When compiling such a report, the most likely compile error would be casting issues. Jasperreports defaults to java.lang.String, you might need to edit the xml of the report manually to get the correct class at each step.

(I found out this method from some asian site, thankfully the code itself was readable! :-) )

云醉月微眠 2024-08-30 06:47:58

可能有更简单和直接的解决方案:

我正在使用 Jasper Reports Studio(Eclipse 插件)。

  1. 最终没有为我工作:-( ...(参见 1.我的答案下面的评论 - 可能是一个错误)您只需检查[x]数据在交叉表属性中预先排序
    当然,只有当您不需要根据预先排序的结果集在报告中的其他位置进行另一次排序时,这才有效。

  2. 使用不可见的交叉表组标题,这是相当棘手的:

    1. 创建新的行/列组(例如通过大纲视图),保留初始名称 Row Group1 或类似的名称
    2. 将其总位置设置为(我们不希望每行/列生成总列)
    3. 将 XML 中的组移至旧组之前
    4. 将群组重命名为某个发音名称,例如name="invisible sort column ..."
      • (XML 中不应再存在对此组的进一步引用)
    5. 如果您在群组中使用群组总计总排名 != )那么您必须基本上将这些总计元素/设置移动到第一个虚拟排序组,因为否则总计将不再显示在每个组列/行之后的总计(=每第二组总计),例如(这里只用列组来展示,行组也遵循同样的原理)

      a|b|c|sum a|sum|b|sum|c|sum
      ==========> =================
      1|2|3|6 1|1 |2|2 |3|3
      
      • 最简单的方法可能是在 XML 中执行此操作,类似于此转换(不要忘记移动 totalPosition=...columnTotalGroup=... 属性并更改总和(如果适用于您的场景)$V{SomeSum_..._ALL}):

        <前><代码>...
        ...
        <交叉表TotalColumnHeader>
        <单元格内容/>>

        ...
        <列组名称=“X”...totalPosition=“结束”>
        ...
        <交叉表TotalColumnHeader>
        <单元格内容...>
        ...

        ...

        ...

        ...

        =>

        <前><代码>...
        ...
        <交叉表TotalColumnHeader>
        <单元格内容...>
        ...

        ...
        <列组名称=“X”...>
        ...
        <交叉表TotalColumnHeader>
        <单元内容/>>

        ...

        ...
        ;
        ...

        ...

    6. (可以跳过:)删除不必要生成的 单元格

      • 最好将报告与以前的版本进行比较,以便可靠、快速地识别 XML 中的这些位置
      • 也许这不是关键的一步,但查看现有的 XML 已经够令人困惑的了;-)
    7. 添加排序列的(存储桶)表达式例如 $F{ORDER_FOR_X}
      • 不要忘记将值类名称分配给java.lang.Integer或此处适合您值的任何内容(看看在您的数据集中分配哪种类型(如果您通过某些列使用它)
    8. 添加一些变量表达式到原始组的Order By Expression中,例如$V{ORDER_FOR_X}

      • $V{...} 就是窍门,不要使用 $F{...}
      • (编辑说它无效,但它会起作用)
      • 意思是如果您可以提供一些定义排序并与要排序的列值相关的字段,例如(Oracle SQL)

        从对偶中选择 1 作为 order_for_x,'foo' 作为 x,'bla blu' 作为 y
        union all 从对偶中选择 2, 'bar', 'ta tu'
        union all select 2, 'bar', 'na na' 从对偶中
        union all select 1, 'foo', 'check it' 从 Dual
        union all 选择 3, 'queue', 'sap' from Dual
        
      • 否则你当然也可以在这里使用其他东西

      • 如果你应该得到一些

        <前><代码>...
        引起原因:java.lang.NullPointerException
        在 org.apache.commons.collections.comparators.ComparableComparator.compare(ComparableComparator.java:92)
        在net.sf.jasperreports.crosstabs.fill.BucketExpressionOrderer.compareOrderValues(BucketExpressionOrderer.java:70)
        ...

        您可以简单地将表达式更改为 $V{ORDER_FOR_X} == null ? 0 : $V{ORDER_FOR_X} 应该可以解决问题

    9. 将虚拟组的所有高/宽字段设置为0,并将文本字段打印当表达式false

    10. (也许可以通过与之前的报告版本进行比较来检查是否没有其他任何更改,以确保您没有弄乱其他内容)
  3. 使用基于度量总计的排序 (如底部链接所述)

  4. 使用 >自定义 Java Comparator 类(如 Pieter VN 2011-11-16 的答案中所述

我发现的进一步可能有用的链接:

There may be much easier and straight forward solutions:

I am using Jasper Reports Studio (Eclipse Plugin).

  1. did not finally work for me :-( ... (see 1. comment below my answer - may be a bug) you just can check [x] Data Pre Sorted in the Crosstab Properties.
    Of course this only works if you do not need another ordering somewhere else in the report based on the pre-sorted result set.

  2. using an invisible crosstab group header, which is rather tricky:

    1. create a new row/column group (e.g. via Outline view), leave the initial name Row Group1 or similar as is for now
    2. set its Total Position to None (we do not want to have a total column per row/column generated)
    3. move the group in the XML before the old one
    4. rename the group to some speaking name, e.g. name="invisible sort column ..."
      • (no further references to this group should exist in the XML anymore)
    5. if you are using group totals on your group (Total Position != None) then you have to basically move these totalling elements/settings to the first dummy sort group, because otherwise the totals will be no totals anymore (= per 2nd group totals) displayed after each group column/row, e.g. (here only shown with column group, but row group follows the same principle)

      a|b|c|sum      a|sum|b|sum|c|sum
      =========  =>  =================
      1|2|3|6        1|1  |2|2  |3|3
      
      • easiest may be to do this in the XML similar to this transformation (do not forget to move the totalPosition=... and columnTotalGroup=... attributes and to change the sum if applicable to your scenario $V{SomeSum_..._ALL}):

        ...
        <columnGroup name="OrderXDummy" height="0">
          ...
          <crosstabTotalColumnHeader>
            <cellContents/>
          </crosstabTotalColumnHeader>
        </columnGroup>
        ...
        <columnGroup name="X" ... totalPosition="End">
          ...
          <crosstabTotalColumnHeader>
            <cellContents ...>
              ...
            </cellContents>
          </crosstabTotalColumnHeader>
        </columnGroup>
        ...
        <crosstabCell ... columnTotalGroup="X">
          ...
           <textFieldExpression><![CDATA[$V{SomeSum_X_ALL}]]></textFieldExpression>
          ...
        </crosstabCell>
        

        =>

        ...
        <columnGroup name="OrderXDummy" height="0" totalPosition="End">
          ...
          <crosstabTotalColumnHeader>
            <cellContents ...>
              ...
            </cellContents>
          </crosstabTotalColumnHeader>
        </columnGroup>
        ...
        <columnGroup name="X" ... >
          ...
          <crosstabTotalColumnHeader>
            <cellContents/>
          </crosstabTotalColumnHeader>
          ...
        </columnGroup>
        ...
        <crosstabCell ... columnTotalGroup="OrderXDummy">
          ...
           <textFieldExpression><![CDATA[$V{SomeSum_OrderXDummy_ALL}]]></textFieldExpression>
          ...
        </crosstabCell>
        
    6. (may be skipped:) remove unnecessarily generated <crosstabCell ... column/rowTotalGroup="..."> cells

      • its maybe best to compare the report with a previous version to reliably and fast identify these spots in the XML
      • maybe this is not a crucial step, but looking at the existing XML is confusing enough ;-)
    7. add the (bucket) expression of your sort column, e.g. $F{ORDER_FOR_X}
      • don't forget to assign Value Class Name to java.lang.Integer or whatever fits for your values here (have a look at your dataset which type is assigned there if your are using it via some column)
    8. add some variable expression to the Order By Expression of the original group, e.g. $V{ORDER_FOR_X}

      • $V{...} is the trick, do not use $F{...}!
      • (the editor says it's invalid, but it will work)
      • meaning if you can provide some field that defines the sort and relates to your to-be-sorted column value, e.g. (Oracle SQL)

        select            1 as order_for_x,  'foo' as x,  'bla blu' as y  from dual
        union all select  2,                 'bar',       'ta tu'         from dual
        union all select  2,                 'bar',       'na na'         from dual
        union all select  1,                 'foo',       'check it'      from dual
        union all select  3,                 'queue',     'sap'           from dual
        
      • otherwise you can of course use something else here as well

      • if you should get some

        ...
        Caused by: java.lang.NullPointerException
        at org.apache.commons.collections.comparators.ComparableComparator.compare(ComparableComparator.java:92)
        at net.sf.jasperreports.crosstabs.fill.BucketExpressionOrderer.compareOrderValues(BucketExpressionOrderer.java:70)
        ...
        

        you can simply change the expression to $V{ORDER_FOR_X} == null ? 0 : $V{ORDER_FOR_X} which should do the trick

    9. set all high/width fields of the dummy group to 0 and the text fields Print When Expression to false

    10. (maybe check via compare with the previous report version that nothing else has changed to make sure you didn't mess up something else)
  3. using an order by based on measure totals (as described in the bottom link)

  4. using a custom Java Comparator class (as described in the answer from Pieter VN 2011-11-16)

further maybe helpful links I found:

只涨不跌 2024-08-30 06:47:58

orderByExpression

通常对交叉表列进行排序的方法是使用 orderByExpression

  1. 为您想要排序的内容定义一个度量(另一个字段或和表达式,请在我的中使用正确的类定义)如果是 Integer),
<measure name="orderByField_measure" class="java.lang.Integer">
     <measureExpression><![CDATA[$F{orderByField}]]></measureExpression>
</measure>
  1. 请使用 bucket 中的 orderByExpression 中的度量
<columnGroup name="myColumnGroup" height="10">
      <bucket class="java.lang.String">
        <bucketExpression><![CDATA[$F{MyField}]]></bucketExpression>
        <orderByExpression><![CDATA[$V{orderByField_measure}]]></orderByExpression>
    </bucket>
    .....
</columnGroup>

comparatorExpression

设置 Comparator(在java es中创建一个类。MyCustomComparator 实现了 Comparator

<bucket class="java.lang.String">
    <bucketExpression><![CDATA[$F{MyField}]]></bucketExpression>
    <comparatorExpression><![CDATA[new com.my.package.MyCustomComparator()]]></comparatorExpression>
</bucket>

Comparable

如果您要显示自己对象的值($F{MyField} 是用户定义的对象),您可以简单地实现 Comparable 以显示您喜欢的顺序。

orderByExpression

The way to order crosstab columns normally is by using the orderByExpression

  1. Define a measure for what you like to order on (another field or and expression, use correct class definition in my case a Integer)
<measure name="orderByField_measure" class="java.lang.Integer">
     <measureExpression><![CDATA[$F{orderByField}]]></measureExpression>
</measure>
  1. Use the measure in orderByExpression in the bucket
<columnGroup name="myColumnGroup" height="10">
      <bucket class="java.lang.String">
        <bucketExpression><![CDATA[$F{MyField}]]></bucketExpression>
        <orderByExpression><![CDATA[$V{orderByField_measure}]]></orderByExpression>
    </bucket>
    .....
</columnGroup>

comparatorExpression

Set a Comparator (create a class in java es. MyCustomComparator that implements Comparator)

<bucket class="java.lang.String">
    <bucketExpression><![CDATA[$F{MyField}]]></bucketExpression>
    <comparatorExpression><![CDATA[new com.my.package.MyCustomComparator()]]></comparatorExpression>
</bucket>

Comparable

If you are displaying value from own object ($F{MyField} is a user defined object) you can simple implement Comparable to display the order as you like.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文