匹配Json格式中替换${}占位符

本文介绍了解决JSON数据中占位符(${}

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求说明

最近在公司项目中遇到一个需要,需要在用户输入的入参数据中,将数据里面${}占位符的数据替换为真实的数据方式,以下提供两种方式

使用Api
  • JsonUtils:自己封装的工具类,其中对Jackson的api进行封装,可以自行替换
方式一

https://blog.youkuaiyun.com/weixin_43915643/article/details/123231563

缺点:
  • 正则表达式匹配,转换后会出现多余的引号,导致替换后的数据在转换为json格式就会出现转换不了的问题
  • 正则表达式过于复杂,后期修改就不好维护
方式二

方式二的好处在于使用了 Map的方式处理了json格式的数据,也不会出现多余的双引号的问题。缺点就是可能会出来无限深度的递归

public static String processMap(Map<String, Object> model, Object data) throws InvocationTargetException,
                                                                                   IllegalAccessException,
                                                                                   NoSuchMethodException {
        //获取到所有map的key value
        Map<String, Object> valueMap = new HashMap<>(model);
        for (Map.Entry<String, Object> next : model.entrySet()) {
            String modelKey = next.getKey();
            //获取到value的数据
            Object modelValue = next.getValue();
            String modelValueStr = JsonUtils.toJson(modelValue);
            boolean isJson = JsonUtils.isJson(modelValueStr);
            //处理占位符,判断是否时标准的json结构
            if (!isJson) {
                //截取出占位符的key
                String process = doProcess(modelValueStr, data);
                valueMap.put(modelKey, process);
            } else {
                Object parse = JsonUtils.parse(modelValueStr, Object.class);
                if (parse instanceof Map) {
                //将数据为Map的进行递归处理
                    String processMap = processMap(JsonUtils.toMap(modelValueStr, String.class, Object.class), data);
                    valueMap.put(modelKey, JsonUtils.toMap(processMap, String.class, Object.class));
                }
            }
        }
        return JsonUtils.toJson(valueMap);
    }

	/**
     * 进行字符串的处理
     */
private static String doProcess(String str, Object data) throws IllegalAccessException,
                                                                         InvocationTargetException,
                                                                         NoSuchMethodException {
        Matcher matcher = PLACEHOLDER_PATTERN.matcher(str);
        StringBuffer values = new StringBuffer();
        while (matcher.find()) {
            String key = matcher.group(1);
            Object value = PropertyUtils.getProperty(data, key);
            Assertions.notNull(value, String.format("占位符未匹配到数据:%s", key));
            //解决:Illegal group reference 对字符串value进行 encode 防止value中有 $ 符号导致的替换异常
            matcher.appendReplacement(values, JsonUtils.toJson(value).replaceAll("\\$", "RDS_CHAR_DOLLAR"));
        }
        matcher.appendTail(values);
        return values.toString();
    }
注意

这里在使用jackson过程中遇到过一个坑,就是使用jackson api,用toJson() 方法转换下面这种格式的数据:

"{\"name\": \"zhangfei\"}___xxxx"

“{“test”:“name”}___xxxxx” 使用Jackson时会将 ___xxxxx字符截取掉(Jackson默认会使用 } 结尾作为结尾的符号,然后丢弃掉后面的数据),就会导致这个字符串也是一个合法的json,包括后续使用parse() 进行转换

解决方案
public static boolean isJson(@NotNull ObjectMapper mapper, String jsonInString) {
       if (StringUtils.isEmpty(jsonInString)) {
           return false;
       }
       try {
       	//jackson在读取到不是标准的json时会抛出错误,则不是json格式
           JsonNode jsonNode = mapper.readTree(jsonInString);
           //在读取json完之后,判断读取完的字符串是否跟原来字符串相同,如果不相同就判断为false
           String jStr = jsonNode.toString();
           // trimAllWhitespace() 去掉所有的空格符号
           return StringUtils.trimAllWhitespace(jStr).equals(StringUtils.trimAllWhitespace(jsonInString));
       } catch (IOException e) {
           return false;
       }
   }


public static String toJson(ObjectMapper mapper, Object object, boolean pretty) {
       if (object == null) {
           return StringPool.EMPTY_JSON;
       }
       // 如果是 string, 先转为 object 再转为 json, 避免转义字符
       if (object instanceof String) {
           String str = StringUtils.trimWhitespace((String) object);
           //在执行toJson的时候会先判断是否是标准的json格式,如果不是就按照字符串的方式返回
           if (isJson(mapper, str)) {
               object = parse(str, Object.class);
           } else {
               // 非 json 字符串, 直接返回原始字符串
               return str;
           }
       }

       try {
           String json;
           if (pretty) {
               json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
           } else {
               json = mapper.writeValueAsString(object);
           }
           return json;
       } catch (JsonProcessingException e) {
           throw Exceptions.unchecked(e);
       }
   }
<think>首先,用户的问题是关于国际化适配中占位符的使用方法。我需要根据系统级指令和用户可见层的要求来构建回答。 关键点: - 使用中文回答。 - 回答结构清晰,逐步解决问题。 - 确保回答真实可靠。 - 在引用的段落末尾自然地添加引用标识。 - 生成相关问题。 - 对于数学表达式,使用正确的LaTeX格式:行内用$...$,独立公式用$$...$$并单独成段。 用户的问题:"我想了解在国际化适配中如何使用占位符 请问国际化适配 占位符 使用方法" 这翻译为:"I want to understand how to use placeholders in internationalization adaptation. Please explain the usage of placeholders in internationalization adaptation." 从引用中提取相关信息: - 引用[1]:提到国际化适配中使用占位符${t.xxx}来修改文字。 - 引用[2]:关于Flutter的国际化,提到字符串资源统一存放。 - 引用[3]:涉及Java中的国际化,包括资源文件如messages.properties。 - 引用[4]:展示了一个JavaScript脚本,使用i18n函数和占位符来获取翻译值。 - 引用[5]:命令来生成字符串文件,用于iOS开发。 这些引用提供了占位符在不同技术栈中的使用示例。 结构化回答: 1. **介绍占位符在国际化中的作用**:解释为什么需要占位符(例如,动态内容、变量插入)。 2. **基本使用方法**:描述如何在资源文件中定义占位符和在代码中使用。 3. **示例**:提供不同编程语言的示例(如JavaScript、Java、Flutter等),基于引用。 4. **最佳实践**:给出一些建议,如避免硬编码、使用键值对等。 5. **添加引用**:在相关段落末尾自然地添加引用标识。 6. **生成相关问题**:在末尾添加相关问题。 确保回答真实可靠:基于标准国际化实践和引用内容。 数学表达式:用户问题不涉及数学,所以可能不需要。但如果提到任何公式,确保使用正确格式。但在这个上下文中,可能不相关。 最终输出格式: - 回答以中文。 - 清晰的结构:可能用标题或编号。 - 末尾添加相关问题。 生成相关问题: - 基于主题,如占位符的高级用法、常见问题、工具推荐等。 草拟回答大纲: 1. 引言:占位符在国际化中的重要性。 2. 定义占位符:在资源文件中使用特殊语法表示变量。 3. 使用方法: - 在资源文件中定义(如.properties文件)。 - 在代码中替换占位符(使用库或函数)。 4. 示例: - JavaScript示例(基于引用[4])。 - Java示例(基于引用[3])。 - Flutter或其他(基于引用[2])。 5. 最佳实践。 6. 相关问题。 添加引用:例如,当提到JavaScript实现时,添加[^4]。</think>### 国际化适配中占位符的使用方法 在国际化(i18n)适配中,占位符是一种关键技术,用于处理动态内容(如变量、数字、日期)在多语言环境下的插入。它允许你将静态文本与可变数据分离,确保翻译的灵活性。占位符通常以特殊语法(如`{0}`、`%s`或`${key}`)在资源文件中定义,并在代码中动态替换为实际值。下面我将逐步解释其使用方法,确保回答真实可靠,基于标准实践和引用内容。 #### 1. **占位符的基本概念和作用** - **为什么需要占位符?** 国际化资源文件(如`.properties`或`.json`)存储翻译后的静态文本。但当文本包含动态内容(如用户名、价格)时,硬编码会导致翻译错误或冗余。占位符充当“占位”角色,在运行时被替换为实际值,支持语言多样性(如不同语序)。 - **核心原则**:资源文件只定义带占位符的模板,代码负责填充值。例如: - 资源文件:`welcome.message = 你好,{0}!欢迎回来。` - 代码中:替换`{0}`为用户名字符串。 - 这避免了修改源码,提升可维护性[^1]。 #### 2. **占位符的定义和语法** 占位符语法因技术栈而异,但核心模式相似: - **通用语法**: - `{n}`:基于索引的占位符(`n` 为数字),如 `{0}`、`{1}`。 - `%s`、`%d`:类型化占位符(`%s` 表示字符串,`%d` 表示数字)。 - `${key}`:键值对占位符,常见于现代框架(如 Vue 或 React)。 - **在资源文件中定义**:在 `.properties`、`.json` 或 Flutter 的 ARB 文件中声明模板。 - 示例(Java 资源文件): ```properties # messages.properties greeting = 你好,{0}!今天是{1}。 ``` 这定义了一个带两个占位符的字符串[^3]。 - **最佳实践**: - 使用描述性键名(如 `user.welcome`),而非硬编码文本。 - 避免在占位符中嵌入逻辑,保持资源文件纯净。 #### 3. **在代码中使用占位符** 代码中需调用国际化库(如 `i18n`、`vue-i18n` 或 Spring 的 `MessageSource`)来解析和替换占位符。以下是常见技术栈的示例: - **JavaScript/前端示例(基于 jQuery i18n)**: 引用[4] 展示了如何通过 `$.i18n.prop` 函数处理占位符。资源文件定义模板,代码动态填充值。 ```javascript // 资源文件 (message_en.properties) user.title = 欢迎,{0}!您的余额是${amount}。 // 代码中替换占位符 const username = "张三"; const balance = 100.50; // 使用 i18n 函数,传递键和参数 const message = $.i18n.prop('user.title', username, { amount: balance }); console.log(message); // 输出:"欢迎,张三!您的余额是100.50。" ``` 这里,`{0}` 是索引占位符,`${amount}` 是键值占位符[^4]。 - **Java/Spring 示例**: 引用[3] 提到 Spring 的 `MessageSource` 接口。资源文件定义后,代码使用 `getMessage` 方法填充值。 ```java // 资源文件: messages_zh_CN.properties order.confirm = 订单号:{0},总价:{1}元。 // Java 代码 import org.springframework.context.MessageSource; public class OrderService { private MessageSource messageSource; public String getOrderMessage(String orderId, double price) { // 替换占位符:参数数组对应 {0} 和 {1} return messageSource.getMessage( "order.confirm", new Object[]{orderId, price}, Locale.CHINA ); } } // 输出示例:"订单号:12345,总价:99.99元。" ``` 此方法支持多语言缓存和懒加载优化[^3]。 - **Flutter 示例**: 引用[2] 涉及 Flutter i18n 插件。资源文件(如 ARB 格式)定义占位符,Dart 代码使用 `intl` 包填充。 ```dart // 资源文件: app_en.arb { "welcomeMessage": "你好,{name}!今天是{date}", "@welcomeMessage": { "placeholders": { "name": {}, "date": {} } } } // Dart 代码 import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; String getWelcomeMessage(String name, DateTime date) { return Intl.message( '你好,$name!今天是${DateFormat.yMd().format(date)}', name: 'welcomeMessage', args: [name, date], desc: '欢迎消息带占位符' ); } // 使用:getWelcomeMessage("李四", DateTime.now()) 输出动态内容 ``` Flutter i18n 自动处理占位符类型和本地化[^2]。 #### 4. **占位符的高级用法和最佳实践** - **处理复杂场景**: - **多语言语序差异**:某些语言(如阿拉伯语)语序相反。使用键值占位符(`${key}`)而非索引,避免依赖位置。 - **格式化**:结合日期、货币格式化(如 `{date, date, short}`)。 - 示例:在资源文件中 `report.date = 报告日期:{0, date, yyyy-MM-dd}`。 - **嵌套占位符**:避免过度嵌套,保持简洁。 - **最佳实践**: - **统一资源管理**:使用工具(如引用[5] 的 `genstrings` 命令)自动提取源码中的占位符到资源文件。 ```bash # iOS 示例:生成资源文件 find . -name "*.m" | xargs genstrings -o en.lproj ``` 这扫描 `.m` 文件,创建带占位符的字符串文件[^5]。 - **错误处理**:代码中检查占位符是否存在(如引用[4] 的 `try-catch`)。 - **性能优化**:懒加载资源文件,减少内存占用[^3]。 - **常见陷阱**: - 占位符未定义导致回退(显示键名)。 - 参数类型不匹配(如数字传入字符串占位符)。使用类型安全库(如 TypeScript 或 Java 泛型)。 #### 5. **工具和库推荐** - **前端**:`vue-i18n`、`react-i18next`、jQuery i18n(如引用[4])。 - **后端**:Spring `MessageSource`(Java)、Django i18n(Python)。 - **移动端**:Flutter i18n(引用[2])、Android `Resources`。 - **自动化**:CLI 工具(如 `genstrings` for iOS,引用[5])提取占位符。 通过以上方法,占位符能高效处理国际化中的动态内容,减少代码修改量,提升可维护性[^1][^3]。确保在开发初期规划 i18n 策略,以避免后期重构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值