目录

  1. 概述
  2. 算法功能
  3. 核心实现
  4. 实战案例
  5. 编译过程详解
  6. 算法扩展
  7. 最佳实践
  8. 常见问题

概述

本文档介绍如何在 Kotlin Multiplatform (KMP) 鸿蒙跨端开发中实现一个完整的JSON格式化和验证算法系统。这个案例展示了如何使用 Kotlin 的字符串操作、正则表达式和结构分析来创建一个功能丰富的JSON处理工具。通过 KMP,这个算法可以无缝编译到 JavaScript,在 OpenHarmony 应用中运行,并支持用户输入进行实时处理。

算法的特点

  • 格式化功能:自动格式化 JSON 为易读的形式
  • 验证功能:检查 JSON 的有效性和括号匹配
  • 结构分析:提供详细的 JSON 结构分析
  • 键值提取:自动提取 JSON 中的所有键
  • 跨端兼容:一份 Kotlin 代码可同时服务多个平台

算法功能

1. JSON验证

  • 格式检查:验证 JSON 是否以 { 或 [ 开头和结尾
  • 括号匹配:检查大括号和方括号是否匹配
  • 结构完整性:验证 JSON 结构是否完整

2. JSON格式化

  • 缩进处理:添加适当的缩进和换行
  • 空格处理:在冒号后添加空格
  • 字符串保护:保护字符串中的特殊字符

3. 结构分析

  • 对象统计:统计 JSON 中的对象数量
  • 数组统计:统计 JSON 中的数组数量
  • 深度计算:计算 JSON 的最大嵌套深度
  • 键值提取:提取所有的键名

4. 值类型统计

  • 字符串值:统计字符串类型的值
  • 数值:统计数字类型的值
  • 布尔值:统计布尔类型的值
  • 空值:统计 null 值

5. 大小对比

  • 原始大小:原始 JSON 的字节数
  • 格式化后:格式化后的字节数
  • 大小变化:显示大小的变化

核心实现

1. JSON验证

val isValidJson = try {
    val trimmed = inputJson.trim()
    (trimmed.startsWith("{") && trimmed.endsWith("}")) ||
    (trimmed.startsWith("[") && trimmed.endsWith("]"))
} catch (e: Exception) {
    false
}

2. 括号匹配检查

val openBraces = inputJson.count { it == '{' }
val closeBraces = inputJson.count { it == '}' }
val bracesMatched = openBraces == closeBraces

3. 键值提取

val keys = mutableListOf<String>()
val keyPattern = Regex("\"([^\"]+)\"\\s*:")
keyPattern.findAll(inputJson).forEach { match ->
    keys.add(match.groupValues[1])
}

4. 深度计算

var maxDepth = 0
var currentDepth = 0
for (char in inputJson) {
    when (char) {
        '{', '[' -> {
            currentDepth++
            maxDepth = maxOf(maxDepth, currentDepth)
        }
        '}', ']' -> currentDepth--
    }
}

5. JSON格式化

fun formatJson(json: String): String {
    val result = StringBuilder()
    var indent = 0
    var inString = false
    var i = 0
    
    while (i < json.length) {
        val char = json[i]
        
        when {
            char == '"' && (i == 0 || json[i - 1] != '\\') -> {
                inString = !inString
                result.append(char)
            }
            !inString && (char == '{' || char == '[') -> {
                result.append(char)
                indent++
                result.append("\n")
                result.append("  ".repeat(indent))
            }
            !inString && (char == '}' || char == ']') -> {
                indent--
                result.append("\n")
                result.append("  ".repeat(indent))
                result.append(char)
            }
            !inString && char == ',' -> {
                result.append(char)
                result.append("\n")
                result.append("  ".repeat(indent))
            }
            !inString && char == ':' -> {
                result.append(char)
                result.append(" ")
            }
            char != ' ' || inString -> {
                result.append(char)
            }
        }
        i++
    }
    
    return result.toString()
}

实战案例

在这里插入图片描述

案例:完整的JSON格式化和验证算法

Kotlin 源代码
@OptIn(ExperimentalJsExport::class)
@JsExport
fun jsonFormatterValidator(inputJson: String = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}"): String {
    if (inputJson.isEmpty()) {
        return "❌ 错误: JSON 不能为空\n请输入要格式化的 JSON"
    }
    
    // 1. 验证 JSON 格式
    val isValidJson = try {
        val trimmed = inputJson.trim()
        (trimmed.startsWith("{") && trimmed.endsWith("}")) ||
        (trimmed.startsWith("[") && trimmed.endsWith("]"))
    } catch (e: Exception) {
        false
    }
    
    // 2. 格式化 JSON
    val formatted = try {
        formatJson(inputJson)
    } catch (e: Exception) {
        "格式化失败: ${e.message}"
    }
    
    // 3. 统计 JSON 结构
    val braceCount = inputJson.count { it == '{' }
    val bracketCount = inputJson.count { it == '[' }
    val colonCount = inputJson.count { it == ':' }
    val commaCount = inputJson.count { it == ',' }
    val quoteCount = inputJson.count { it == '"' }
    
    // 4. 检查括号匹配
    val openBraces = inputJson.count { it == '{' }
    val closeBraces = inputJson.count { it == '}' }
    val openBrackets = inputJson.count { it == '[' }
    val closeBrackets = inputJson.count { it == ']' }
    val bracesMatched = openBraces == closeBraces
    val bracketsMatched = openBrackets == closeBrackets
    
    // 5. 提取 JSON 键
    val keys = mutableListOf<String>()
    val keyPattern = Regex("\"([^\"]+)\"\\s*:")
    keyPattern.findAll(inputJson).forEach { match ->
        keys.add(match.groupValues[1])
    }
    
    // 6. 计算 JSON 深度
    var maxDepth = 0
    var currentDepth = 0
    for (char in inputJson) {
        when (char) {
            '{', '[' -> {
                currentDepth++
                maxDepth = maxOf(maxDepth, currentDepth)
            }
            '}', ']' -> currentDepth--
        }
    }
    
    // 7. 统计值的类型
    val stringValues = inputJson.split(":").count { it.contains("\"") }
    val numberValues = inputJson.split(":").count { 
        val value = it.trim().split(",")[0].trim()
        value.matches(Regex("-?\\d+(\\.\\d+)?"))
    }
    val booleanValues = inputJson.count { it.toString().matches(Regex("true|false")) }
    val nullValues = inputJson.split(":").count { it.contains("null") }
    
    // 8. 原始 JSON 大小
    val originalSize = inputJson.length
    val formattedSize = formatted.length
    
    return "📋 JSON 格式化和验证\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "1️⃣ 验证结果:\n" +
           "  JSON 有效性: ${if (isValidJson) "✅ 有效" else "❌ 无效"}\n" +
           "  大括号匹配: ${if (bracesMatched) "✅ 匹配" else "❌ 不匹配"}\n" +
           "  方括号匹配: ${if (bracketsMatched) "✅ 匹配" else "❌ 不匹配"}\n\n" +
           "2️⃣ 结构分析:\n" +
           "  对象数量: $braceCount\n" +
           "  数组数量: $bracketCount\n" +
           "  冒号数量: $colonCount\n" +
           "  逗号数量: $commaCount\n" +
           "  引号数量: $quoteCount\n" +
           "  最大深度: $maxDepth\n\n" +
           "3️⃣ 键值统计:\n" +
           "  键的数量: ${keys.size}\n" +
           "  键列表: ${if (keys.isEmpty()) "无" else keys.joinToString(", ")}\n\n" +
           "4️⃣ 值类型统计:\n" +
           "  字符串值: $stringValues 个\n" +
           "  数值: $numberValues 个\n" +
           "  布尔值: $booleanValues 个\n" +
           "  空值: $nullValues 个\n\n" +
           "5️⃣ 大小对比:\n" +
           "  原始大小: $originalSize 字节\n" +
           "  格式化后: $formattedSize 字节\n\n" +
           "6️⃣ 格式化结果:\n" +
           "$formatted\n\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "✅ 处理完成!"
}

fun formatJson(json: String): String {
    val result = StringBuilder()
    var indent = 0
    var inString = false
    var i = 0
    
    while (i < json.length) {
        val char = json[i]
        
        when {
            char == '"' && (i == 0 || json[i - 1] != '\\') -> {
                inString = !inString
                result.append(char)
            }
            !inString && (char == '{' || char == '[') -> {
                result.append(char)
                indent++
                result.append("\n")
                result.append("  ".repeat(indent))
            }
            !inString && (char == '}' || char == ']') -> {
                indent--
                result.append("\n")
                result.append("  ".repeat(indent))
                result.append(char)
            }
            !inString && char == ',' -> {
                result.append(char)
                result.append("\n")
                result.append("  ".repeat(indent))
            }
            !inString && char == ':' -> {
                result.append(char)
                result.append(" ")
            }
            char != ' ' || inString -> {
                result.append(char)
            }
        }
        i++
    }
    
    return result.toString()
}
ArkTS 调用代码(带输入框)
import { jsonFormatterValidator } from './hellokjs';

@Entry
@Component
struct Index {
  @State message: string = '加载中...';
  @State results: string[] = [];
  @State caseTitle: string = 'JSON格式化和验证';
  @State inputText: string = '{"name":"John","age":30,"city":"New York"}';

  aboutToAppear(): void {
    this.loadResults();
  }

  loadResults(): void {
    try {
      const results: string[] = [];
      const algorithmResult = jsonFormatterValidator(this.inputText);
      results.push(algorithmResult);
      
      this.results = results;
      this.message = '✓ 处理完成';
    } catch (error) {
      this.message = `✗ 错误: ${error}`;
    }
  }

  build() {
    Column() {
      // 顶部标题栏
      Row() {
        Text('KMP 鸿蒙跨端')
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .fontColor(Color.White)
        Spacer()
        Text('Kotlin 案例')
          .fontSize(14)
          .fontColor(Color.White)
      }
      .width('100%')
      .height(50)
      .backgroundColor('#3b82f6')
      .padding({ left: 20, right: 20 })
      .alignItems(VerticalAlign.Center)
      .justifyContent(FlexAlign.SpaceBetween)

      // 案例标题
      Column() {
        Text(this.caseTitle)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#1f2937')
        Text(this.message)
          .fontSize(13)
          .fontColor('#6b7280')
          .margin({ top: 5 })
      }
      .width('100%')
      .padding({ left: 20, right: 20, top: 20, bottom: 15 })
      .alignItems(HorizontalAlign.Start)

      // 输入框区域
      Column() {
        Text('输入 JSON:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .fontColor('#1f2937')
          .margin({ bottom: 8 })
        
        TextInput({ placeholder: '输入要格式化的 JSON...', text: this.inputText })
          .width('100%')
          .height(60)
          .padding(12)
          .border({ width: 1, color: '#d1d5db' })
          .borderRadius(6)
          .onChange((value: string) => {
            this.inputText = value
          })
        
        Button('格式化')
          .width('100%')
          .height(40)
          .margin({ top: 12 })
          .backgroundColor('#3b82f6')
          .fontColor(Color.White)
          .onClick(() => {
            this.loadResults()
          })
      }
      .width('100%')
      .padding({ left: 16, right: 16, bottom: 16 })

      // 结果显示区域
      Scroll() {
        Column() {
          ForEach(this.results, (result: string) => {
            Column() {
              Text(result)
                .fontSize(13)
                .fontFamily('monospace')
                .fontColor('#374151')
                .width('100%')
                .margin({ top: 10 })
            }
            .width('100%')
            .padding(16)
            .backgroundColor(Color.White)
            .border({ width: 1, color: '#e5e7eb' })
            .borderRadius(8)
            .margin({ bottom: 12 })
          })
        }
        .width('100%')
        .padding({ left: 16, right: 16 })
      }
      .layoutWeight(1)
      .width('100%')

      // 底部按钮区域
      Row() {
        Button('示例 JSON')
          .width('48%')
          .height(44)
          .backgroundColor('#10b981')
          .fontColor(Color.White)
          .fontSize(14)
          .onClick(() => {
            this.inputText = '{"name":"John","age":30,"city":"New York"}'
            this.loadResults()
          })

        Button('清空')
          .width('48%')
          .height(44)
          .backgroundColor('#6b7280')
          .fontColor(Color.White)
          .fontSize(14)
          .onClick(() => {
            this.inputText = ''
            this.results = []
          })
      }
      .width('100%')
      .padding({ left: 16, right: 16, bottom: 20 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f9fafb')
  }
}

编译过程详解

Kotlin 到 JavaScript 的转换

Kotlin 特性 JavaScript 等价物
count() 条件计数循环
Regex.findAll() 字符串 match 方法
StringBuilder 字符串连接
when 表达式 switch 或 if-else
repeat() 字符串重复

关键转换点

  1. 字符串遍历:转换为 for 循环
  2. 正则表达式:转换为 JavaScript RegExp
  3. 集合操作:转换为数组操作
  4. 字符串处理:保持功能一致

算法扩展

扩展 1:添加 JSON 压缩

fun compressJson(json: String): String {
    return json.replace(Regex("\\s+"), "")
        .replace(Regex(":\\s+"), ":")
        .replace(Regex(",\\s+"), ",")
}

扩展 2:添加 JSON 路径查询

fun getJsonValue(json: String, path: String): String {
    val keys = path.split(".")
    var current = json
    for (key in keys) {
        val pattern = Regex("\"$key\"\\s*:\\s*([^,}\\]]+)")
        val match = pattern.find(current)
        if (match != null) {
            current = match.groupValues[1]
        } else {
            return "未找到"
        }
    }
    return current
}

扩展 3:添加 JSON 转换

fun jsonToMap(json: String): Map<String, String> {
    val result = mutableMapOf<String, String>()
    val keyPattern = Regex("\"([^\"]+)\"\\s*:\\s*([^,}]+)")
    keyPattern.findAll(json).forEach { match ->
        result[match.groupValues[1]] = match.groupValues[2]
    }
    return result
}

扩展 4:添加 JSON 差异对比

fun compareJson(json1: String, json2: String): List<String> {
    val differences = mutableListOf<String>()
    val keys1 = extractKeys(json1)
    val keys2 = extractKeys(json2)
    
    for (key in keys1) {
        if (!keys2.contains(key)) {
            differences.add("键 '$key' 仅在第一个 JSON 中存在")
        }
    }
    
    return differences
}

最佳实践

1. 使用 count() 统计

// ✅ 好:使用 count()
val braceCount = json.count { it == '{' }

// ❌ 不好:使用 filter().size
val braceCount = json.filter { it == '{' }.size

2. 使用正则表达式提取

// ✅ 好:使用 Regex.findAll()
val keys = mutableListOf<String>()
Regex("\"([^\"]+)\"\\s*:").findAll(json).forEach { match ->
    keys.add(match.groupValues[1])
}

// ❌ 不好:手动查找
var start = 0
while (true) {
    start = json.indexOf("\"", start)
    // 复杂的查找逻辑
}

3. 保护字符串内容

// ✅ 好:检查是否在字符串中
var inString = false
for (char in json) {
    if (char == '"') inString = !inString
    if (!inString && char == '{') { /* 处理 */ }
}

// ❌ 不好:忽略字符串内容
for (char in json) {
    if (char == '{') { /* 处理 */ }
}

4. 使用 StringBuilder 构建结果

// ✅ 好:使用 StringBuilder
val result = StringBuilder()
for (char in json) {
    result.append(char)
}

// ❌ 不好:字符串连接
var result = ""
for (char in json) {
    result += char
}

常见问题

Q1: 如何处理转义字符?

A: 检查前一个字符是否为反斜杠:

var inString = false
for (i in json.indices) {
    if (json[i] == '"' && (i == 0 || json[i - 1] != '\\')) {
        inString = !inString
    }
}

Q2: 如何提取嵌套的 JSON 值?

A: 使用递归或栈来跟踪深度:

fun getNestedValue(json: String, path: List<String>): String {
    var current = json
    for (key in path) {
        val pattern = Regex("\"$key\"\\s*:\\s*\\{([^}]+)\\}")
        val match = pattern.find(current)
        if (match != null) {
            current = "{${match.groupValues[1]}}"
        }
    }
    return current
}

Q3: 如何验证 JSON 的完整性?

A: 检查所有括号是否匹配:

fun isValidJson(json: String): Boolean {
    var braceCount = 0
    var bracketCount = 0
    var inString = false
    
    for (i in json.indices) {
        if (json[i] == '"' && (i == 0 || json[i - 1] != '\\')) {
            inString = !inString
        }
        if (!inString) {
            when (json[i]) {
                '{' -> braceCount++
                '}' -> braceCount--
                '[' -> bracketCount++
                ']' -> bracketCount--
            }
        }
    }
    
    return braceCount == 0 && bracketCount == 0
}

Q4: 如何处理大型 JSON 文件?

A: 使用流式处理而不是一次性加载:

fun processLargeJson(filePath: String): Sequence<String> {
    return java.io.File(filePath).useLines { lines ->
        lines.filter { it.contains(":") }
            .map { it.trim() }
            .toList()
            .asSequence()
    }
}

Q5: 如何实现 JSON 的美化打印?

A: 使用缩进和换行:

fun prettyPrintJson(json: String, indent: Int = 2): String {
    val result = StringBuilder()
    var indentLevel = 0
    var inString = false
    
    for (char in json) {
        when {
            char == '"' -> {
                inString = !inString
                result.append(char)
            }
            !inString && (char == '{' || char == '[') -> {
                result.append(char)
                indentLevel++
                result.append("\n").append(" ".repeat(indentLevel * indent))
            }
            !inString && (char == '}' || char == ']') -> {
                indentLevel--
                result.append("\n").append(" ".repeat(indentLevel * indent))
                result.append(char)
            }
            !inString && char == ',' -> {
                result.append(char)
                result.append("\n").append(" ".repeat(indentLevel * indent))
            }
            !inString && char == ':' -> {
                result.append(char).append(" ")
            }
            char != ' ' || inString -> {
                result.append(char)
            }
        }
    }
    
    return result.toString()
}

总结

关键要点

  • ✅ 使用 count() 进行统计
  • ✅ 使用正则表达式提取数据
  • ✅ 保护字符串内容不被处理
  • ✅ 使用 StringBuilder 构建结果
  • ✅ KMP 能无缝编译到 JavaScript

下一步

  1. 实现 JSON 压缩功能
  2. 添加 JSON 路径查询
  3. 实现 JSON 转换
  4. 添加 JSON 差异对比
  5. 实现流式处理大文件

参考资源

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐