目录
[为什么用 Json 过渡](#为什么用 Json 过渡)
特殊之处
《C#实现数据导出任一Word图表的通用呈现方法及一些体会》一文讲解了数据导出生成 WORD 图表的通用方法, 根据应用需求,大部分的图表(如柱形图)如下设置:

插入后,微缩版 Excel 应用显示了一些通用的数据设置,类别项和系列项的数值,改变其中的项和系列值,图表就会产生相应的变化。
对于像单一数据来源的,如雷达图:

可以设置必要项,分类和系列1,而不会因为系列2的数据存在而出现错误,WORD会自动"忽略"。但最近在新需求应用中,使用一款散点类型图表时发现了一些问题,其EXCEL数据源如下:

与柱状图、饼状图的格式不同,散点图是 X值和Y值的列名+二维数组设置。但实际的操作实现可能需要进行类似如下的设置:

即模拟去掉列名,直接输出数据,可正常进行显示输出 ,因此我们需要对通用方法进行一些改造。
设计方案
仍遵循原设计方案基础,将业务数据存入一个二维字符串数组,并转化为Json数据格式,并添加一个查找关键字节点,假设为" t:chart1"。在 Word 模板插入散点图图表,图表的标题设置为Json对应的查找关键字,即" t:chart1"。最后,读取Word模板文件,遍历模板文件中的图表对象,并按查找关键字与图表的标题进行对比,匹配成功,则将JSON中数组转化为图表需要的EXCEL数组形式,完成图表绘制输出。
经调试,首列单元格需要设置为数字格式,因些增加了Json配置 " t:chart1_ex ",例如下代码:
cs
writer.WritePropertyName("t:chart1_ex");
writer.WriteStartArray();
writer.WriteStartObject();
writer.WritePropertyName("NumberFormat");
writer.WriteValue("#,##0");
writer.WriteEndObject();
writer.WriteEndArray();
为什么用 Json 过渡
我们的云架构里设计了一个 Office 计算中心,在某些环境下,比如 Linux 中需要这种方式传递并返回值,以达到导入导出Office文件的目的。大家可根据实际的应用进行设计,这里仅作为参考。
开发环境
操作系统:Windows Server 2019 DataCenter
开发工具:VisualStudio2019
框架及语言:.net 4.7.1 C#
服务上需要安装 Office 2016或以上
关键代码实现
假设文件模板中的散点图表如下,关键查找字(图表标题)设为 " t:chart1",如下图:

(1)创建Json格式
这里引入 Newtonsoft.Json.dll 程序集进行操作,代码如下:
cs
StringWriter sw = new StringWriter();
using (Newtonsoft.Json.JsonWriter writer = new Newtonsoft.Json.JsonTextWriter(sw))
{
writer.Formatting = Newtonsoft.Json.Formatting.Indented;
writer.WriteStartObject();
//t:chart1
writer.WritePropertyName("t:chart1");
writer.WriteStartArray();
//point1
writer.WriteStartObject();
writer.WritePropertyName("y");
writer.WriteValue((int)y);
writer.WritePropertyName("x");
writer.WriteValue((int)x);
writer.WriteEndObject();
writer.WriteStartObject();
writer.WriteEndArray();
writer.WritePropertyName("t:chart1_ex");
writer.WriteStartArray();
writer.WriteStartObject();
writer.WritePropertyName("NumberFormat");
writer.WriteValue("#,##0");
writer.WriteEndObject();
writer.WriteEndArray();
//t:chart1
writer.WriteEndObject();
writer.Flush();
}
sw.Close();
string jsonContent = sw.GetStringBuilder().ToString();
(2)查找图表并设置单元格数字格式
本代码程序只显示示例片断,非完整程序,仅供参考:
cs
////一些引用
using Word=Microsoft.Office.Interop.Word;
using Newtonsoft.Json.Linq;
////转换 json 字符串为 json 对象
Newtonsoft.Json.Linq.JObject jObject = null;
if (jsonContent != "")
{
try
{
jObject = Newtonsoft.Json.Linq.JObject.Parse(jsonContent); //转换为json对象
}
catch (Exception e)
{
resultReport += "create json object fail.<br>"; //失败记入调试报告
}
}
////初始化 Word 应用程序
Word.Application WordApp=new Word.Application();
//创建一个名为WordDoc的文档对象
WordApp.DisplayAlerts=Word.WdAlertLevel.wdAlertsNone; //禁止一切提示警告
//打开 filename 的文件
Word.Document WordDoc=WordApp.Documents.Open(ref filename,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing);
//禁用拼写检查
WordDoc.SpellingChecked = false;
WordDoc.ShowSpellingErrors = false;
////遍历word 里的 shapes
for (int i = 1; i <= WordDoc.InlineShapes.Count; i++)
{
Word.InlineShape shape = WordDoc.InlineShapes[i]; //得到 shape对象
//遍历 json 对象
foreach (var item in jObject)
{
string tcmd = item.Key.ToString(); //取关键字
//如果 shape 包含图表,则继续
if (shape.HasChart == Microsoft.Office.Core.MsoTriState.msoTrue)
{
//如果图表未设置标题,则短路
if (shape.Chart.HasTitle == false)
{
continue;
}
//获取图表的title
string _name = shape.Chart.ChartTitle.Text.Trim().ToLower();
if (_name.IndexOf(tcmd) != -1) //如果包含关键字则继续
{
//替换掉关键字,保留下来的是真正的标题
shape.Chart.ChartTitle.Text = shape.Chart.ChartTitle.Text.Replace(tcmd, "");
//这是一个玄机,否则会报错,目前我是这样的解决,A1:Z100,先赋值为空串
shape.Chart.ChartData.Workbook.Worksheets[1].Range("A1:Z100").Value = "";
//计算最后的单元格地址
string lastcellAddress = "$" + ((char)(64 + jObject[tcmd][0].Count())).ToString() + "$" + jObject[tcmd].Count().ToString();
//获得最终地址字串
string sourceDataAddress = "='Sheet1'!$A$1:" + lastcellAddress;
//计算附加配置,设置单元格数字格式字串
string numberFormat = "#,##0.0";
if (jObject[_name + "_ex"] != null)
{
if (jObject[_name + "_ex"][0]["NumberFormat"] != null)
{
numberFormat = jObject[_name + "_ex"][0]["NumberFormat"].ToString();
}
}
//遍历json对象节点里的数组
for (int i = 0; i < jObject[tcmd].Count(); i++)
{
List<JToken> tokens = jObject[tcmd][i].ToList();
int k = 0;
foreach (JToken jToken in tokens)
{
//为每一个单元格赋值
string celladdress = ((char)(65 + k)).ToString() + (i + 1).ToString();
//设置单元格数字格式
shape.Chart.ChartData.Workbook.Worksheets[1].Range(celladdress).NumberFormat = numberFormat;
shape.Chart.ChartData.Workbook.Worksheets[1].Range(celladdress).Value = jToken.ToArray()[0].ToString();
k++;
}
}
shape.Chart.SetSourceData(sourceDataAddress); //设置更新图表的数据源
break;
} // index of name
} // has chart
}//foreach tcmd
} //WordDoc.InlineShapes
小结
图表数据的正确呈现受限于Word的提供能力,需要细心的测试、调试,比如
shape.Chart.ChartData.Workbook.Worksheets[1].Range("A1:Z100").Value = ""; 的初始化,散点图首列的设置等。
如有理解错误或更好的解决方案,欢迎大家讨论分享,批评指正。