Aspose.words Mail Merge之Region Mail merge

转载请注明:
http://blog.csdn.net/sinat_30276961/article/details/48346479

上一篇Aspose.words Mail Merge初识我大致介绍了什么是Mail merge以及怎么通过Aspose使用基本的mail merge。本篇将进一步讲解它的更强大的功能–Region Mail Merge。

在上一篇中,我们已经了解到Mail merge可以实现把文档里的Merge field替换成数据。在最后,我举了个例子,讲解了最基本的Mail merge。

那么问题来了:

  • 什么是Region Mail Merge?
  • 它和上一篇的实例的区别在哪?
  • 要怎么使用Region Mail Merge?

好,带着问题来开始往下看吧。

什么是Region Mail Merge

Region Mail Merge,翻译成中文的意思差不多是:区域邮件合并。先根据字面意思猜下大概是:选择文档的部分区域进行邮件合并。OK,其实根据字面意思猜的是正确的。

不过在Aspose官网,它给出的例子往往会误导读者,以为Region Mail Merge是用来重复添加数据的。

举个例子来讲,如下图是模板文档:

这里写图片描述

然后它的最后Mail merge结果是:

Aspose.words Mail Merge之Region Mail merge_第1张图片

上面的图片就是官网的图片,从图片和文字表述中,我得出Region Mail Merge就是区域重复Merge。

事实上,我的这个最初的理解是错误的。重复这个概念是一直存在于Mail merge的。也就是说,默认就有重复这个技能。

我们再回到上一篇的例子来看看。

Aspose.words Mail Merge之Region Mail merge_第2张图片

在上一篇中,我有提到moveNext,这个是用来决定需要重复几次merge。

OK,我们就来重复一下看看,我把moveNext的代码稍作修改:

    class MyMailMergeDataSource implements IMailMergeDataSource {
        int index = 0;
        boolean once = true;
        int repeatCount = 2;
        ......

        @Override
        public boolean moveNext() throws Exception {
            if (repeatCount > 0) {
                repeatCount--;
                index = 0;
                return true;
            }
            return false;
        }

代码很简单,我们直接看效果:

Aspose.words Mail Merge之Region Mail merge_第3张图片

下面看不到,只能截取部分,不过不影响,至少看到重复的效果了。

从这个结果,不难退出,它的重复是针对整个文档的。我们可以对照看下DOM树:

Aspose.words Mail Merge之Region Mail merge_第4张图片

OK,产生了两个Section,也就是说是针对整个Section重复的。

看到这里,就很清晰了,重复的概念是一直存在的。那官网给的鬼讲解,鬼例子是干嘛呢!

扯远了~~,继续往下。

使用Region Mail Merge

既然已经清楚了Region Mail Merge的功能,那么我们就可以开工了。

比方说现在头给你了个任务,需要创建一个文档,它需要某部分区域重复的添加很多相同类型的数据,就如我上面贴的官网的那两张图片。

那么要怎么弄呢?还是和上篇讲的流程一样,先是创建模板。

创建模板

那怎么创建模板呢?既然是区域Mail Merge,那肯定需要标识符吧。

对的!区域mail merge,需要在模板里建立标识符,这样Aspose才能识别出来。标识符就是TableStart:XXX和TableEnd:XXX,这个XXX指的是TableName,如果上一篇看得仔细,那可能会联想到有个方法getTableName,对了,就是那个。TableName前后要一致。

很简单吧,我就直接上图了:

Aspose.words Mail Merge之Region Mail merge_第5张图片

这里,我限定了两个区域。

  • 第一个是在第一个表格的一行数据里。它的TableName是NormalTable。
  • 第二个是包含整个表格。它的TableName是XTable。

这里需要指出的是,Aspose里虽然取名叫TableName,但实际上它不单单只能使用在表格上,它可以使用在任何你想限定的区域,只要在同一个Section里,不要被我的例子给忽悠了。我只是举了个表格的例子

然后在这些限定区域里,我都创建了四个Merge field。偷懒了,上面的和下面的都一样的。

这里需要指出一下TableStart和TableEnd标识符的使用范围。

  • 这两个标识符必须在同一个Section里。正常情况下,我们的文档只有一个Section,除非像我上面尝试修改的例子的那样,才会出现两个或多个Section。
  • 如果标识符定义在表格里,那它们必须在同一行里。
  • 标识符可以嵌套使用。这个在后面会再讲。
  • 标识符要成对出现,并且它们的名称要一致。

ok,模板已经创建完了。接着就是写代码了。

代码实现

在官网,它给的介绍和例子都是涉及到java的数据库的,这个数据库在android端是无法使用的,所以对我们Android开发没有用。不过,我会稍微讲下,过一遍。

要执行区域邮件合并,需要用到Document.getMailMerge().executeWithRegions()这个方法。它接受如下数据类型。

这里写图片描述

IMailMergeDataSource我们已经了解了,那么其他几个呢?官网只讲了上面两个,也对,第三个在最基本的Mail merge已经涉及到过。

java端的数据源

DataSet
说到这个,我们还要从ResultSet说起。使用过java的数据库的猿们肯定知道ResultSet,没错,它是java.sql.ResultSet,是个接口。它是用来访问数据库表用的。

那DataSet和ResultSet什么关系呢?好吧,我们还要从DataTable说起。DataTable是Aspose里的,它抽象成数据表。然后呢,DataTable包含一个ResultSet。

而DataSet可以包含多个DataTable。也即是说,DataSet里包含了多个区域需要Merge的数据源。

DataTable
上面讲到,DataTable里包含ResultSet。并且DataTable就是一个区域的数据源。也即是说,DataTable里维护一个TableName,定义它所属的是哪个Region。

ok,这两个的基本概念就是这样。它们是用来把数据库里的数据抽象到区域Mail merge的数据源。我贴些代码示例:

public void executeWithRegionsDataTable() throws Exception
{
  Document doc = new Document(getMyDir() + "MailMerge.ExecuteWithRegions.doc");

  int orderId = 10444;

  // Perform several mail merge operations populating only part of the document each time.

  // Use DataTable as a data source.
  // The table name property should be set to match the name of the region defined in the document.
  com.aspose.words.DataTable orderTable = getTestOrder(orderId);
  doc.getMailMerge().executeWithRegions(orderTable);

  com.aspose.words.DataTable orderDetailsTable = getTestOrderDetails(orderId, "ExtendedPrice DESC");
  doc.getMailMerge().executeWithRegions(orderDetailsTable);

  doc.save(getMyDir() + "MailMerge.ExecuteWithRegionsDataTable Out.doc");
}

private static com.aspose.words.DataTable getTestOrder(int orderId) throws Exception
{
  java.sql.ResultSet resultSet = executeDataTable(java.text.MessageFormat.format(
      "SELECT * FROM AsposeWordOrders WHERE OrderId = {0}", Integer.toString(orderId)));

  return new com.aspose.words.DataTable(resultSet, "Orders");
}

private static com.aspose.words.DataTable getTestOrderDetails(int orderId, String orderBy) throws Exception
{
  StringBuilder builder = new StringBuilder();

  builder.append(java.text.MessageFormat.format(
      "SELECT * FROM AsposeWordOrderDetails WHERE OrderId = {0}", Integer.toString(orderId)));

  if ((orderBy != null) && (orderBy.length() > 0))
  {
    builder.append(" ORDER BY ");
    builder.append(orderBy);
  }

  java.sql.ResultSet resultSet = executeDataTable(builder.toString());
  return new com.aspose.words.DataTable(resultSet, "OrderDetails");
}

/**
 * Utility function that creates a connection, command,
 * executes the command and return the result in a DataTable.
 */
private static java.sql.ResultSet executeDataTable(String commandText) throws Exception
{
  Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");// Loads the driver

  // Open the database connection.
  String connString = "jdbc:odbc:DRIVER={Microsoft Access Driver (*.mdb)};" +
      "DBQ=" + getDatabaseDir() + "Northwind.mdb" + ";UID=Admin";

  // From Wikipedia: The Sun driver has a known issue with character encoding and Microsoft Access databases.
  // Microsoft Access may use an encoding that is not correctly translated by the driver, leading to the replacement
  // in strings of, for example, accented characters by question marks.
  //
  // In this case I have to set CP1252 for the european characters to come through in the data values.
  java.util.Properties props = new java.util.Properties();
  props.put("charSet", "Cp1252");

  // DSN-less DB connection.
  java.sql.Connection conn = java.sql.DriverManager.getConnection(connString, props);

  // Create and execute a command.
  java.sql.Statement statement = conn.createStatement();
  return statement.executeQuery(commandText);
}

Android和java端的数据源

当然,上面这些只适用于java开发猿,对于android端没啥意义。接着,我就讲重点了,就是Android端怎么写。

这就要用到我上篇讲到过的接口IMailMergeDataSource。

代码其实差不多,只是有稍微不同,我先贴出来:

public class RegionMailMergeActivity extends FragmentActivity{
    @Override
    protected void onCreate(@Nullable Bundle arg0) {
        super.onCreate(arg0);
        setContentView(R.layout.activity_region_mail_merge);

        new Thread(new RegionMailMergeTask()).start();
    }

    class RegionMailMergeTask implements Runnable {

        @Override
        public void run() {
            try {
                Document doc = new Document(MyApplication.filePath
                        + File.separator + "TestRegionMailMerge.doc");
                doc.getMailMerge().executeWithRegions(new NormalTableDataSource());
                doc.getMailMerge().executeWithRegions(new XTableDataSource());
                doc.save(MyApplication.filePath 
                        + File.separator + "RegionMailMergeResult.doc");
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

OK,稍作解释。照例要加载模板,然后就是执行Region mail merge。这里执行了两个区域的。接下去就看实现的接口类:

public class RegionMailMergeActivity extends FragmentActivity{
    private static final String[][] DATAS = new String[][] {
        new String[] {
                "熊猫", "中国四川及一些动物园", "四川", "中国"
        },
        new String[] {
                "科比", "意大利某个地方", "洛杉矶", "美国"
        },
        new String[] {
                "隔壁老王", "这家伙无处不在,就在你隔壁", "无法统计", "放弃统计"
        },
        new String[] {
                "阎罗王", "冥界十殿之第五殿", "这个咋整", "冥界"
        },
        new String[] {
                "旺财", "我家就有一个", "地球人存在的地方就有汪星人存在", "汪星人国度"
        }
    };
    ......

    class NormalTableDataSource implements IMailMergeDataSource {
        int repeatCount = 5;

        int i = -1, j = 0;

        @Override
        public IMailMergeDataSource getChildDataSource(String name)
                throws Exception {
            return null;
        }

        @Override
        public String getTableName() throws Exception {
            return "NormalTable";
        }

        @Override
        public boolean getValue(String field, Object[] objects) throws Exception {
            objects[0] = DATAS[i][j++];
            return true;
        }

        @Override
        public boolean moveNext() throws Exception {
            if (repeatCount > 0) {
                repeatCount--;
                i++;
                j = 0;
                return true;
            }
            return false;
        }
    }

    class XTableDataSource implements IMailMergeDataSource {
        int repeatCount = 5;

        int i = -1, j = 0;

        @Override
        public IMailMergeDataSource getChildDataSource(String name)
                throws Exception {
            return null;
        }

        @Override
        public String getTableName() throws Exception {
            return "XTable";
        }

        @Override
        public boolean getValue(String field, Object[] objects) throws Exception {
            objects[0] = DATAS[i][j++];
            return true;
        }

        @Override
        public boolean moveNext() throws Exception {
            if (repeatCount > 0) {
                repeatCount--;
                i++;
                j = 0;
                return true;
            }
            return false;
        }
    }

还是偷懒使用静态数据做数据源。然后实现IMailMergeDataSource这个接口。

这里创建了两个类,分别对应NormalTable区域的数据源和XTable的数据源。可以看到,我设定的重复次数是5次。

这里需要注意:getTableName()要返回对应区域的Tablename。moveNext的j游标初始化别忘了。其余和上篇介绍的没区别。

我们直接上结果图:

Aspose.words Mail Merge之Region Mail merge_第6张图片

后面因为一屏幕的篇幅有限,没有显示出来。结果正是我想要的。

好了,关于Region Mail merge讲完了。哦,对了,还有一个落了,IMailMergeDataSourceRoot。这个其实是IMailMergeDataSource的多集。

如果DataTable相对于IMailMergeDataSource,那么DataSet相对于IMailMergeDataSourceRoot。懂了吧。

啥,还不懂。那我换种说法,IMailMergeDataSourceRoot可以包含多个IMailMergeDataSource,也就是说它支持多个区域的一次性搞定。这样一来,上面的例子我就不需要写两次executeWithRegions了。懂了吧,好了,Region mail merge讲到这。

下一篇Aspose.words Mail Merge之Nest Mail merge讲解嵌套邮件合并,有兴趣就点吧。

你可能感兴趣的:(Aspose相关)