Qt JSON实战指南:解析、序列化与避坑要点
很多刚接触Qt的开发者会在Json选型上纠结到底用自带的QJsonDocument还是引入第三方Json库。我的建议很简单——如果项目本身就是Qt技术栈自带的Json支持完全够用而且从Qt 5到Qt 6这套API一直稳定保留。这篇文章不打算抄官方文档里的示例而是按我实际写业务时的顺序复盘怎么解析、怎么构造、怎么落盘、哪些细节最容易出问题最后再聊聊业务对象和Json互转的封装思路。内容适合刚入门Qt、想快速把Json用起来的开发者也适合写了一段时间但总在编码、精度这些地方犯迷糊的朋友。1. 先弄清QJson家族的分工后面代码才不晕1.1 四个核心类各管哪一块Qt对Json的支持主要有四个类QJsonDocument、QJsonObject、QJsonArray、QJsonValue。它们的关系很好理解我打个比方QJsonDocument是整个Json文档的容器好比一个文件夹QJsonObject对应一个大括号{}包裹的键值对区域好比文件夹里的一页表格QJsonArray对应中括号[]包裹的有序列表好比表格上的一列数据而QJsonValue则是单元格里面的值可以是字符串、数字、布尔值、null也可以是嵌套的QJsonObject或QJsonArray。第一次接触的人常犯的错误是纠结于读取Json时到底该先创建QJsonObject还是QJsonDocument。顺序其实很固定任何Json解析第一步都是把原始字节交给QJsonDocument::fromJson()得到一个文档对象然后再从文档里取出顶层对象或顶层数组真正操作数据的阶段才轮到QJsonObject和QJsonArray。反过来当你从零构造一段Json时是先建QJsonObject和QJsonArray拼好数据最后用QJsonDocument包一层再输出成字符串。理解这个解析自上而下、构造自下而上的方向后面基本不会乱。1.2 为什么QJsonValue一个类能吃下所有类型QJsonValue是这套API里的接口人。它内部用type()方法告诉我们当前到底存的是什么同时提供了isObject()、isArray()、isString()、isDouble()、isBool()、isNull()、isUndefined()这一串判断方法。很多业务Bug就出在判断类型这件事上。尤其要注意isNull()和isUndefined()完全是两码事。举个例子如果Json里写的是name: null那么value(name)返回的QJsonValue是Null类型isNull()返回true但如果键根本不存在比如value(age)返回的是UndefinedisUndefined()为trueisNull()反而为false。很多人在取一个可能缺失的字段时习惯用isNull()做判断这会把字段不存在和字段值为null混为一谈后面业务逻辑就容易走错分支。我后面会单独展开讲这一点因为它真的很坑。QJsonValue还有一个容易被忽略的小细节它本身是值语义的类拷贝开销很小。所以在嵌套取值的时候把中间结果直接存成QJsonValue再继续取代码会清爽不少也不需要到处声明临时QJsonObject变量。2. 字符串解析从fromJson到逐层取值的标准流程2.1 错误检查别只检查是否失败还要看offset无论数据来自文件、网络还是用户输入只要拿到字节串统一都用QJsonDocument::fromJson()处理。这个函数接受QByteArray返回一个QJsonDocument出错时会把错误信息填到QJsonParseError里。QByteArray rawData ...; // 可能是文件读出来的也可能是接口返回的 QJsonParseError parseError; QJsonDocument doc QJsonDocument::fromJson(rawData, parseError); if (parseError.error ! QJsonParseError::NoError) { qWarning() Json解析失败: parseError.errorString() 位置: parseError.offset; return; }这里我有一个小习惯除了看errorString()一定会同时把parseError.offset打出来。offset是出错时在原始字符串中的偏移量联调时特别有用。尤其当数据是从服务端返回的时候返回一段好几千行的Json如果只有一句unexpected token根本不知道去哪里找问题有了offset直接到原始文本对应位置看一下多半能立刻发现是引号没配对还是多了一个逗号。还有一点QJsonDocument::fromJson()接收的是QByteArray不是QString。也就是说如果你手里是QString要先通过toUtf8()转成字节而不是直接传入。很多网络接口框架已经把响应体转成了QString这时候别偷懒一定要先转UTF-8再解析。2.2 一层一层取值的代码模板解析成功之后先要判断顶层结构是对象还是数组。实际业务里顶层基本都是对象但偶尔也有接口直接返回数组所以稳妥写法是if (doc.isObject()) { QJsonObject rootObj doc.object(); // 继续用 rootObj 取字段 } else if (doc.isArray()) { QJsonArray rootArray doc.array(); // 当成数组处理 }在对象里取值最核心的方法就是value()。我给的模板是这样的QString name rootObj.value(name).toString(); int age rootObj.value(age).toInt(); bool isVip rootObj.value(isVip).toBool();注意toInt()、toString()这些转换方法的默认行为是尽力转换。比如Json里age是28这个字符串它也能尽量变成整数28如果类型完全不匹配则返回默认值数字返回0字符串返回空串。这个尽力转换在快速开发时很省事但在严格校验的场合反而是隐患。如果你希望严格一点可以先用isDouble()、isString()判断类型再调用转换方法。嵌套的复杂Json才是日常。假设数据结构是这样的{ user: { profile: { nickname: 老张, phone: 13800000000 }, tags: [qt, json] } }取值可以连续调用中途不需要反复创建临时变量QJsonObject rootObj doc.object(); QString nickname rootObj.value(user).toObject() .value(profile).toObject() .value(nickname).toString();这里有个隐藏知识点value(user)返回的是QJsonValue我们要先toObject()得到QJsonObject然后才能继续value()。如果某个中间层不存在后面的toObject()会返回一个空对象再取下一层就得到Undefined最终toString()返回空串不会导致程序崩溃。也就是说Qt这套API在链式取值时天然是安全的不像某些语言里访问null属性直接抛异常。但代价是一旦字段名写错你拿到的不是异常而是默默的空值Bug更难发现。2.3 顶层是数组怎么办顶层是数组的情况常见于批量查询接口比如返回一批设备列表。处理方式就是遍历QJsonArrayQJsonArray arr doc.array(); for (const QJsonValue item : arr) { if (!item.isObject()) { continue; } QJsonObject obj item.toObject(); QString id obj.value(id).toString(); QString name obj.value(name).toString(); // 处理每一行 }这里我建议在循环里加一层isObject()判断。看似多余但实际数据里混入null或纯数字会让程序措手不及。尤其从第三方接口拿数据时数组元素可能是不规则的做好防御式检查能省掉不少线上问题。3. 数据落盘构造Json、序列化与安全写入文件3.1 声明式构造嵌套对象和数组从零构造Json时Qt的QJsonObject支持初始化列表写起来很直观。比如我要构造上面那个用户对象QJsonObject profileObj; profileObj.insert(nickname, 老张); profileObj.insert(phone, 13800000000); QJsonArray tagsArr; tagsArr.append(qt); tagsArr.append(json); QJsonObject userObj; userObj.insert(profile, profileObj); userObj.insert(tags, tagsArr); QJsonObject rootObj; rootObj.insert(user, userObj);QJsonObject和QJsonArray也有带初始化列表的构造方式QJsonObject profileObj{ {nickname, 老张}, {phone, 13800000000} }; QJsonArray tagsArr{qt, json}; QJsonObject userObj{ {profile, profileObj}, {tags, tagsArr} };两种写法等价我平时偏爱初始化列表因为它把嵌套结构直接摊开读代码的时候不用来回找插入语句。但要提醒一点insert同一键会覆盖旧值初始化列表里如果出现两个相同的键也一样后面的生效。这种静默覆盖在排查问题时很容易被忽略。构造完别忘了用QJsonDocument包一层QJsonDocument doc(rootObj);到了这一步数据还在内存里真正要落地还需要序列化。3.2 Compact和Indented两种输出怎么选QJsonDocument::toJson()有两个重载默认参数输出紧凑格式也就是去掉所有多余空格和换行传QJsonDocument::Indented则输出带缩进的格式化内容。QByteArray compactData doc.toJson(QJsonDocument::Compact); QByteArray prettyData doc.toJson(QJsonDocument::Indented);Compact格式适合网络传输、存数据库字段、签名计算体积小Indented格式适合写配置文件、写日志、联调时人工阅读。一个常见场景是程序启动时读取配置文件如果程序发现配置缺失会自动生成一份默认配置这种生成一定要用Indented否则用户打开配置文件看一行好几千字符的Json第一反应就是程序坏了。顺便提一句Qt输出Json默认就是UTF-8编码的QByteArray不含中文时是纯ASCII安全含中文时就是标准的UTF-8字节写文件的时候保持字节原样写入即可不要再做额外的编码转换。3.3 用QSaveFile代替QFile写文件写Json文件看起来用QFile就够了但如果你写过需要定期保存的运行配置一定会遇到程序写到一半崩溃配置文件损坏的情况。Qt提供了一个更稳的类QSaveFile。它先把数据写进同一个目录下的临时文件调用commit()时才真正替换目标文件。如果中途崩溃原始文件还能保留。QSaveFile saveFile(/path/to/config.json); if (!saveFile.open(QIODevice::WriteOnly)) { qWarning() 无法打开文件写入: saveFile.errorString(); return; } saveFile.write(doc.toJson(QJsonDocument::Indented)); if (!saveFile.commit()) { qWarning() 写入配置失败: saveFile.errorString(); }注意QSaveFile虽然名字里有File但用法和QFile几乎一样。唯一要记住的是写完一定要调用commit()直接把saveFile销毁相当于放弃本次写入临时文件会被自动清理。我不止一次看到有人写完忘了commit结果文件始终没更新排查了半天。还有一个细节打开文件时不要加QIODevice::Text标志。加了这个标志Windows平台上Qt会自动做换行符转换把\n变成\r\n。Json字符串里如果包含字面换行会受干扰。直接用默认的WriteOnly二进制模式才能保证字节原样落盘。4. 实际项目中踩过坑这里全列出来4.1 UTF-8中文乱码问题出在源文件而不是Json很多人第一次在Qt里构造带中文的Json解析回来发现乱码第一反应是Json不支持中文。其实Json规范本身就是UTF-8QJsonDocument内部也没问题。真正的问题往往出在源码文件编码上。Windows下MSVC编译时如果源文件是GBK编码且没加对应的编译选项老张这样的字符串字面量会以本地编码塞进可执行文件。到了Qt里QString拿到的是被误解的字节序列再转成UTF-8输出自然就乱了。解决办法主要有三个源文件统一保存为UTF-8并开启/utf-8编译选项MSVC所有中文字符串字面量用QStringLiteral(u8老张)包裹强制按UTF-8处理这种方式在需要配合toUtf8()的场景最省心从外部文件读配置时先确认文件是UTF-8必要时读出来手动去掉BOM。另外一个与UTF-8 BOM相关的真实案例Windows上记事本保存UTF-8文件时有可能加BOM头也就是文件开头多了三字节0xEF 0xBB 0xBF。Qt的fromJson()遇到BOM不会自动跳过直接解析会报错。我的做法是读文件后先判断一下QByteArray raw file.readAll(); if (raw.startsWith(\xEF\xBB\xBF)) { raw.remove(0, 3); }去掉BOM再交给fromJson()问题立刻消失。4.2 数字精度超过2^53的整数请用字符串QJsonValue内部存数字用的是double。这意味着超过2^53的整数会丢失精度。现实里踩得最狠的就是订单号、用户ID、雪花ID这类超过16位的数字。QJsonObject obj; obj.insert(orderId, 210123456789012345678);这样插入再序列化出来你可能会发现约等于210123456789012350000。这个精度丢失是double本身的机制不是Qt的Bug。我吃过这个亏之后总结了一条铁律凡是超过2^53范围内的整数或者可能被客户端当字符串处理的ID字段一律按字符串来存。obj.insert(orderId, QStringLiteral(210123456789012345678));这样不仅精度不会丢很多后端语言里ID本身也是字符串对接起来反而更顺畅。相关的还有金额字段如果涉及小数也别依赖浮点运算存字符串或者整数分单位更靠谱。4.3 isUndefined和isNull不是一回事这个坑我开头提过这里展开讲。业务上常见的一种需求是判断某个字段有没有传。类似if (!rootObj.value(remark).isNull()) { // 用户填了备注 }如果Json里是remark: null这个分支不会进入逻辑看着对。但问题在于接口方可能根本不返回remark这个键此时value(remark)返回的是UndefinedisNull()同样为false你的逻辑不会进入。也就是说键值为null和键不存在被混在一起处理了。反过来如果你想判断默认值没传应该用isUndefined()if (rootObj.value(remark).isUndefined()) { // 没传这个字段 }所以我现在的习惯是需要区分没传和传了null的场景一律分别判断isUndefined()和isNull()不需要区分时干脆先统一下业务口径让接口方约定好到底返回不返回这个键免得两边理解不一致。4.4 键顺序和重复键别依赖QJsonObject的遍历顺序QJsonObject的内部存储是按某种规则组织的不是严格的插入顺序。如果你的业务对字段顺序敏感比如要按特定字段顺序拼字符串做签名校验千万别直接用QJsonObject遍历而是自定义一个有序字段列表再按列表取。重复键的问题也容易忽视。解析下面这段Json时a到底取哪个值{ a: 1, a: 2 }Qt的fromJson()在遇到重复键时后面的值会覆盖前面的。大部分第三方接口不会故意这么返回但万一遇到这种脏数据不要想着能拿到两个值只能拿到其中一个。按规范Json里键本就应该是唯一的客户端处理时也没必要为重复键特意设计只要不依赖第一个保留就行。5. 从裸Json到业务对象映射层的设计思路5.1 手写toJson和fromJson最笨但最稳在Qt项目里我见过两种处理Json和业务对象的风格。一种是在使用处散落大量value()和insert()代码一多就混乱另一种是为每个业务类写两个转换函数。以实际项目中的用户信息结构体为例struct UserInfo { QString name; int age 0; QVectorQString tags; bool isVip false; QJsonObject toJson() const { QJsonArray tagArr; for (const QString tag : tags) { tagArr.append(tag); } return QJsonObject{ {name, name}, {age, age}, {tags, tagArr}, {isVip, isVip} }; } void fromJson(const QJsonObject obj) { name obj.value(name).toString(); age obj.value(age).toInt(); isVip obj.value(isVip).toBool(); tags.clear(); QJsonArray tagArr obj.value(tags).toArray(); for (const QJsonValue v : tagArr) { tags.append(v.toString()); } } };这段代码很多人会觉得啰嗦但它的好处是字段的读写规则全部收敛在一个文件里字段改名、加字段、处理兼容性只需要改这里。项目初期字段少时不觉得等几十个字段散落在十几个界面里调用时这种集中式写法能救命。5.2 QVariantMap中转适合快速改改的写法如果你只是临时处理一份配置或者字段不多可以用QVariantMap中转代码写起来短QVariantMap map object.toVariantMap(); map.insert(level, 3); QJsonObject newObject QJsonObject::fromVariantMap(map);toVariantMap()能递归地把嵌套对象、数组转成QVariant反过来也一样。这套写法适合快速改几个字段不适合做完整业务映射因为一旦涉及特定字段的类型转换比如转枚举、转自定义结构体QVariantMap的表达力就不够了。我一般只在调试或做简单配置合并时用它。需要留意的是嵌套层次太多的JsonQVariantMap中转会频繁发生类型转换性能比直接操作QJsonObject略低。对于配置类的小文件可忽略但如果处理的是MB级别的大Json建议直接用QJson家族的类逐层解析。5.3 往返一致性测试比什么都好用给Json映射层写个往返测试是我的保留项目也是最推荐的验证方式。思路很简单构造一个业务对象toJson()变成Json再fromJson()还原成新对象最后比较两个对象的每个字段是否一致。// 测试伪代码 UserInfo original; original.name 老张; original.tags {qt, json}; UserInfo restored; restored.fromJson(original.toJson()); Q_ASSERT(restored.name original.name); Q_ASSERT(restored.tags original.tags);这个测试最大的价值不是测正常数据能不能通而是能暴露出字段漏写、类型不一致、缺省值不匹配这类问题。我在实际项目中用这种方式维护过二十多个结构体的转换逻辑每次接口字段有调整先跑一遍往返测试基本能在合入前拦住大部分低级错误。如果某个结构体里有时间字段、枚举字段更要写进往返测试里用不同的值跑几遍比拍脑袋保证应该没问题靠谱得多。我自己在维护这类映射层时还有一条额外经验接口返回原始Json在解析前后各打一份日志或落一份文件。联调时如果双方对不上直接把dump出来的Json丢给接口方通常一眼就能看出是哪个字段名写错、哪个嵌套层级偏了。配上Qt的Indented格式输出日志可读性也好线上排查问题的效率能提升不少。这几年代码里Json的使用频率越来越高但Qt这套API的设计一直挺稳核心类也没怎么变过。只要把编码、精度、空值判断这几个基本面掌握住再去处理配置文件、接口对接、对象序列化基本不会遇到特别刁钻的问题。如果你正准备在项目里统一Json处理方案我建议就用自己的Qt内置API起步把映射层做薄一点同时把往返测试补上后续就算字段再多心里也有底。

相关新闻

Numpy、OpenCV、PIL.Image 图像格式转换全攻略

Numpy、OpenCV、PIL.Image 图像格式转换全攻略

只要在 Python 里折腾过图像处理,早晚都会碰见这三种格式打架的情况:直接读进 Numpy 的数组、OpenCV 读出来的 BGR 图、PIL 打开的对象。尤其是当你既想用 OpenCV 做形态学处理,又想用 PIL 去生成缩略图,或者想拿 Matplotlib 显示…

2026/10/12 17:52:21 阅读更多 →
麒麟V10 ARM离线部署K8S 1.26.15:依赖链与避坑指南

麒麟V10 ARM离线部署K8S 1.26.15:依赖链与避坑指南

简介:本资源面向需要在国产化信创环境中落地容器编排的运维与云原生工程师,提供基于银河麒麟Kylin V10、ARM架构并采用外部etcd与containerd运行时部署Kubernetes 1.26.15一主多从集群的完整离线材料,可解决ARM平台镜像与依赖包获取困难、离线…

2026/10/12 17:52:21 阅读更多 →
ObjectARX插件上云全指南:从远程桌面到容器化的落地实践

ObjectARX插件上云全指南:从远程桌面到容器化的落地实践

简介:面向AutoCAD二次开发工程师与点云技术研究者,一套ObjectARX应用源码包围绕ObjectARX与AutoCAD云平台结合处理点云数据的完整技术方案展开,提供了可直接编译的C工程,重点实现点云快速加载、渲染、过滤、测量等功能&#xff0c…

2026/10/12 17:52:21 阅读更多 →

最新新闻

三强版 Codex 一键部署包,本地桌面 AI 自动化全流程实操

三强版 Codex 一键部署包,本地桌面 AI 自动化全流程实操

🔍前言 本文介绍 Codex Harness 一键安装包的部署方案,无需解压,也无需繁琐的环境配置,新手也能快速上手。该方案同时支持 Windows 与 macOS 双平台,跟随向导几步即可完成安装、登录与模型密钥配置。文中完整演示了技…

2026/10/13 20:11:08 阅读更多 →
SpringBoot多环境日志配置:用logback-spring.xml+Profile实现环境隔离

SpringBoot多环境日志配置:用logback-spring.xml+Profile实现环境隔离

1. 为什么SpringBoot项目要把日志配置拆成多环境不管你信不信,我见过太多SpringBoot项目,日志配置就一个logback.xml躺在resources目录里,从入职第一周用到项目上线,开发环境一堆DEBUG刷屏没人管,生产环境出了问题想查…

2026/10/13 20:11:07 阅读更多 →
MATLAB中AES-256加解密实战:Java桥接与文件安全

MATLAB中AES-256加解密实战:Java桥接与文件安全

简介:这份资源面向学习密码学与MATLAB编程的读者,提供AES-256位加密算法在MATLAB 2012B环境下的完整实现代码,帮助理解对称加密的内部运作机制。压缩包共16个文件,约448KB,以13个m脚本为核心,涵盖密钥扩展、…

2026/10/13 20:11:07 阅读更多 →
C++结构体与结构体数组:从定义、内存布局到排序实践

C++结构体与结构体数组:从定义、内存布局到排序实践

如果你翻过C教材的目录,多半会在“结构体”这一章停几秒钟,然后想:这不就是把几个变量捆在一起吗?有什么好讲的。我工作之后才意识到,结构体恰恰是C里最容易被低估的基础设施,链表、树、图形控件的属性表、…

2026/10/13 20:11:07 阅读更多 →
Spring Boot JPA 实战指南:从实体映射到查询优化的避坑经验

Spring Boot JPA 实战指南:从实体映射到查询优化的避坑经验

1. 为什么我要认真聊聊 Spring Boot 里的 JPA做后端开发这些年,我接触过的持久层方案不算少。从最早手写 JDBC 拼 SQL,到后来用 MyBatis 写 XML 映射文件,再到 Spring Boot 项目里大量使用 JPA,每一种方案都有它适合的场景。但要说…

2026/10/13 20:11:07 阅读更多 →
Sufer 8.0英文版等值线图实战:从散点插值到批量出图的完整避坑指南

Sufer 8.0英文版等值线图实战:从散点插值到批量出图的完整避坑指南

简介:Surfer 8.0英文版是Golden Software公司推出的专业地形图绘制与三维建模软件,面向地质、环境科学、工程及地球科学领域的从业者与学习者,用于将离散数据转化为连续格网表面并生成专业图件。压缩包共234个文件,约12.41MB&…

2026/10/13 20:10:07 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →