转载请注明:
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,翻译成中文的意思差不多是:区域邮件合并。先根据字面意思猜下大概是:选择文档的部分区域进行邮件合并。OK,其实根据字面意思猜的是正确的。
不过在Aspose官网,它给出的例子往往会误导读者,以为Region Mail Merge是用来重复添加数据的。
举个例子来讲,如下图是模板文档:
然后它的最后Mail merge结果是:
上面的图片就是官网的图片,从图片和文字表述中,我得出Region Mail Merge就是区域重复Merge。
事实上,我的这个最初的理解是错误的。重复这个概念是一直存在于Mail merge的。也就是说,默认就有重复这个技能。
我们再回到上一篇的例子来看看。
在上一篇中,我有提到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;
}
代码很简单,我们直接看效果:
下面看不到,只能截取部分,不过不影响,至少看到重复的效果了。
从这个结果,不难退出,它的重复是针对整个文档的。我们可以对照看下DOM树:
OK,产生了两个Section,也就是说是针对整个Section重复的。
看到这里,就很清晰了,重复的概念是一直存在的。那官网给的鬼讲解,鬼例子是干嘛呢!
扯远了~~,继续往下。
既然已经清楚了Region Mail Merge的功能,那么我们就可以开工了。
比方说现在头给你了个任务,需要创建一个文档,它需要某部分区域重复的添加很多相同类型的数据,就如我上面贴的官网的那两张图片。
那么要怎么弄呢?还是和上篇讲的流程一样,先是创建模板。
那怎么创建模板呢?既然是区域Mail Merge,那肯定需要标识符吧。
对的!区域mail merge,需要在模板里建立标识符,这样Aspose才能识别出来。标识符就是TableStart:XXX和TableEnd:XXX,这个XXX指的是TableName,如果上一篇看得仔细,那可能会联想到有个方法getTableName,对了,就是那个。TableName前后要一致。
很简单吧,我就直接上图了:
这里,我限定了两个区域。
这里需要指出的是,Aspose里虽然取名叫TableName,但实际上它不单单只能使用在表格上,它可以使用在任何你想限定的区域,只要在同一个Section里,不要被我的例子给忽悠了。我只是举了个表格的例子
然后在这些限定区域里,我都创建了四个Merge field。偷懒了,上面的和下面的都一样的。
这里需要指出一下TableStart和TableEnd标识符的使用范围。
ok,模板已经创建完了。接着就是写代码了。
在官网,它给的介绍和例子都是涉及到java的数据库的,这个数据库在android端是无法使用的,所以对我们Android开发没有用。不过,我会稍微讲下,过一遍。
要执行区域邮件合并,需要用到Document.getMailMerge().executeWithRegions()这个方法。它接受如下数据类型。
IMailMergeDataSource我们已经了解了,那么其他几个呢?官网只讲了上面两个,也对,第三个在最基本的Mail merge已经涉及到过。
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);
}
当然,上面这些只适用于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游标初始化别忘了。其余和上篇介绍的没区别。
我们直接上结果图:
后面因为一屏幕的篇幅有限,没有显示出来。结果正是我想要的。
好了,关于Region Mail merge讲完了。哦,对了,还有一个落了,IMailMergeDataSourceRoot。这个其实是IMailMergeDataSource的多集。
如果DataTable相对于IMailMergeDataSource,那么DataSet相对于IMailMergeDataSourceRoot。懂了吧。
啥,还不懂。那我换种说法,IMailMergeDataSourceRoot可以包含多个IMailMergeDataSource,也就是说它支持多个区域的一次性搞定。这样一来,上面的例子我就不需要写两次executeWithRegions了。懂了吧,好了,Region mail merge讲到这。
下一篇Aspose.words Mail Merge之Nest Mail merge讲解嵌套邮件合并,有兴趣就点吧。