You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 07:18:13