如何在Flutter(Android/iOS)中使用XSL实现XML转换?
在Flutter中实现XSL转换XML并生成SHA-256哈希
解决方案思路
由于原生xsltProcessor已废弃,可通过两种方式实现需求:一是使用Dart社区维护的XSLT库,二是通过平台通道调用原生代码(复用你已有的Java实现,iOS端补充对应逻辑)。以下是两种方案的具体实现:
方案一:纯Dart库实现
使用Dart的XML解析库结合XSLT转换库完成逻辑,无需依赖原生代码。
步骤1:添加依赖
在pubspec.yaml中引入所需库:
dependencies: xml: ^6.5.0 xslt: ^0.2.0 # 可替换为最新版本 crypto: ^3.0.3 flutter/services.dart: any
步骤2:实现转换与哈希逻辑
import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:flutter/services.dart'; import 'package:xml/xml.dart'; import 'package:xslt/xslt.dart'; /// 加载assets中的XSLT文件 Future<String> _loadXsltFromAssets() async { return await rootBundle.loadString('assets/invoice.xsl'); } /// 生成发票哈希 Future<String> getInvoiceHash(String xmlDocument) async { // 加载XSLT模板 final xsltContent = await _loadXsltFromAssets(); // 解析XML与XSLT final xmlDoc = XmlDocument.parse(xmlDocument); final xsltDoc = XmlDocument.parse(xsltContent); // 执行XSLT转换,匹配Java代码的输出配置 final transformer = XsltTransformer(xsltDoc); final transformedXml = transformer.transform(xmlDoc).toXmlString( encoding: 'UTF-8', indent: false, omitXmlDeclaration: true, ); // 生成SHA-256哈希并转为Base64 final bytes = utf8.encode(transformedXml); final digest = sha256.convert(bytes); return base64.encode(digest.bytes); }
步骤3:配置assets
在pubspec.yaml中声明XSLT文件路径:
flutter: assets: - assets/invoice.xsl
方案二:平台通道调用原生代码
若XSLT包含复杂语法(如自定义函数、高级模板),Dart库支持有限时,可复用原生代码通过MethodChannel调用。
Android端(复用已有Java代码)
在MainActivity中注册通道并绑定逻辑:
import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugin.common.MethodChannel; public class MainActivity extends FlutterActivity { private static final String CHANNEL = "com.example.invoice/hash"; @Override public void configureFlutterEngine(FlutterEngine flutterEngine) { super.configureFlutterEngine(flutterEngine); new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL) .setMethodCallHandler((call, result) -> { if (call.method.equals("getInvoiceHash")) { String xmlDocument = call.argument("xml"); try { String hash = getInvoiceHash(xmlDocument, this); result.success(hash); } catch (Exception e) { result.error("TRANSFORM_ERROR", e.getMessage(), null); } } else { result.notImplemented(); } }); } // 粘贴你已有的getInvoiceHash方法 public String getInvoiceHash(String xmlDocument, Context mContext) throws TransformerException, IOException { // ... 原有代码保持不变 ... } // 粘贴你已有的getTransformer方法 private Transformer getTransformer(Context mContext) throws TransformerConfigurationException { // ... 原有代码保持不变 ... } }
iOS端(Objective-C实现)
在AppDelegate中注册通道并实现转换逻辑:
#import "AppDelegate.h" #import "GeneratedPluginRegistrant.h" #import <CommonCrypto/CommonCrypto.h> #import <libxml/globals.h> #import <libxml/xslt.h> @implementation AppDelegate NSString *const CHANNEL = @"com.example.invoice/hash"; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController; FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:CHANNEL binaryMessenger:controller.binaryMessenger]; __weak typeof(self) weakSelf = self; [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { if ([call.method isEqualToString:@"getInvoiceHash"]) { NSString *xmlDocument = call.arguments[@"xml"]; NSString *hash = [weakSelf generateInvoiceHash:xmlDocument]; hash ? result(hash) : result([FlutterError errorWithCode:@"TRANSFORM_ERROR" message:@"转换失败" details:nil]); } else { result(FlutterMethodNotImplemented); } }]; [GeneratedPluginRegistrant registerWithRegistry:self]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } - (NSString*)generateInvoiceHash:(NSString*)xmlDocument { // 加载XSLT文件(放在iOS项目Resources目录下) NSString *xsltPath = [[NSBundle mainBundle] pathForResource:@"invoice" ofType:@"xsl"]; xmlDocPtr xmlDoc = xmlParseDoc((const xmlChar*)[xmlDocument UTF8String]); xmlDocPtr xsltDoc = xmlParseFile([xsltPath UTF8String]); xsltStylesheetPtr stylesheet = xsltParseStylesheetDoc(xsltDoc); if (!stylesheet) { xmlFreeDoc(xmlDoc); return nil; } // 执行XSLT转换 xmlDocPtr resultDoc = xsltApplyStylesheet(stylesheet, xmlDoc, NULL); xmlChar *resultBuffer; int bufferLength; xsltSaveResultToString(&resultBuffer, &bufferLength, resultDoc, stylesheet); NSString *transformedStr = [[NSString alloc] initWithBytes:resultBuffer length:bufferLength encoding:NSUTF8StringEncoding]; // 释放资源 xsltFreeStylesheet(stylesheet); xmlFreeDoc(resultDoc); xmlFreeDoc(xmlDoc); xmlFree(resultBuffer); xsltCleanupGlobals(); xmlCleanupParser(); // 生成SHA-256哈希并转Base64 const char *cStr = [transformedStr UTF8String]; unsigned char digest[CC_SHA256_DIGEST_LENGTH]; CC_SHA256(cStr, strlen(cStr), digest); NSData *digestData = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH]; return [digestData base64EncodedStringWithOptions:0]; } @end
Flutter端调用代码
import 'package:flutter/services.dart'; class InvoiceHashService { static const _channel = MethodChannel('com.example.invoice/hash'); static Future<String?> getInvoiceHash(String xmlDocument) async { try { return await _channel.invokeMethod('getInvoiceHash', {'xml': xmlDocument}); } on PlatformException catch (e) { print('错误:${e.message}'); return null; } } } // 使用示例 // final hash = await InvoiceHashService.getInvoiceHash(xmlContent);
注意事项
- 方案一中的Dart库对复杂XSLT语法支持有限,若你的XSLT包含高级特性,优先选择方案二。
- iOS端需在
Podfile中添加pod 'libxml2',并在Build Settings中设置HEADER_SEARCH_PATHS为$(SDKROOT)/usr/include/libxml2。
内容的提问来源于stack exchange,提问作者Scribes Dev
相关产品推荐
相关产品推荐

