1. 一份城市清单背后的数据工程思维
第一次看到"中国所有城市(json数据),按A-Z排序"这个标题,很多人第一反应是:这不就是个数据整理活儿吗?把城市名字列出来,排个序,导出成JSON,完事。但我干了十多年数据相关的工作,可以很负责任地说,越是看起来简单的东西,越容易在细节上翻车。这份城市JSON数据,往小了说是一个前端下拉框的选项来源,往大了说是地址系统、物流分单、区域统计分析、用户画像打标等一系列业务的地基。地基没打好,上面盖什么楼都歪。
先把这个项目的核心价值说清楚。它要交付的是一份结构化的、覆盖全国城市的JSON数据集,并且按照A-Z的字母顺序排列。这里的"A-Z"其实有个隐含前提——中国城市名是中文,中文怎么按A-Z排?答案是转成拼音首字母来排。所以这个项目本质上包含三个核心动作:数据采集与清洗、拼音转换与排序、JSON结构化输出。适合谁来参考?前端开发者需要城市选择器数据、后端开发者要建区域字典表、数据分析师要做地域维度聚合、甚至做跨境电商需要城市映射的,都能从这份数据里受益。哪怕你只是想学一下JSON数据处理和拼音排序的实操思路,这篇内容也能给你一套可以直接抄作业的方案。
我见过太多人在这件事上走弯路。有人直接从某个网页复制粘贴,结果混进了"市辖区"这种非城市条目;有人用Excel排序,中文按Unicode码点排出来完全不是拼音顺序;还有人忽略了直辖市、地级市、县级市之间的层级关系,导致数据用起来一团糟。所以接下来我会把整个流程拆开,从数据来源的选择、清洗规则的设计、拼音转换的坑、排序算法的实现,一直到JSON结构的定义和输出,每一步都讲透。
2. 数据来源选择与采集策略
2.1 为什么数据源决定了后续80%的工作量
做任何数据项目,第一步永远是找数据源。城市数据看起来到处都是,但质量参差不齐。我大致把常见来源分成三类,每类都有各自的适用场景和坑。
第一类是公开的行政区划代码表。这类数据通常以国家标准的形式存在,包含省、市、县三级,每个条目有唯一的行政区划代码。优点是权威、完整、层级清晰;缺点是更新有滞后,而且原始格式往往是表格或者固定宽度文本,需要额外解析。第二类是地图服务商提供的开放数据。这类数据更新及时,格式规范,通常直接就是JSON或GeoJSON,但可能包含一些业务自定义的字段,需要筛选。第三类是社区维护的开源数据集。GitHub上有不少这类仓库,优点是拿来即用,缺点是质量全看维护者心情,有的几年不更新,有的混入了错误条目。
我的建议是:以行政区划代码表为基准,用地图服务商数据做交叉校验。这样既保证了权威性和完整性,又能及时发现过时条目。具体操作上,先把基准数据拉下来,存成CSV或者直接解析,然后逐条和校验源比对,标记出差异项人工确认。
2.2 采集过程中必须处理的边界情况
采集阶段有几个边界情况,不处理干净后面全是麻烦。
- 直辖市怎么算:北京、上海、天津、重庆这四个,它们既是省级单位又是城市。在"城市列表"里,它们应该作为城市出现,而不是作为省份。但它们的下级还有"市辖区",这些区不应该混进城市列表。
- 特别行政区和台湾省的城市:从数据完整性角度,通常会把香港、澳门、台北、高雄等包含进来,但要注意标注清楚,避免和内地城市的管理体系混淆。
- 省直辖县级市:比如某些省份下面有直接由省管辖的县级市,它们不属于任何一个地级市。这类城市容易被漏掉,因为按"省-市-县"三级去遍历时,它们可能挂在省下面而不是市下面。
- 同名城市:中国有不止一组同名的地方,比如有的城市名在不同省份重复出现。如果JSON里只用城市名做key,就会冲突。所以必须保留省份字段或者行政区划代码作为唯一标识。
注意:采集阶段千万不要图省事直接复制网页上的列表。网页展示往往做了合并单元格、省略重复省份名等处理,直接复制会丢失层级信息,后期修复的成本远高于重新采集。
2.3 数据字段的最小完备集设计
采集的时候要想清楚最终JSON里要哪些字段。字段太少不够用,字段太多冗余。根据我的经验,一份通用的城市JSON数据,每个条目至少应该包含以下字段:
| 字段名 | 类型 | 说明 | 是否必填 |
|---|---|---|---|
| code | string | 行政区划代码,唯一标识 | 是 |
| name | string | 城市中文名 | 是 |
| pinyin | string | 城市名全拼 | 是 |
| initial | string | 拼音首字母,大写 | 是 |
| province | string | 所属省份 | 是 |
| level | string | 层级,如"直辖市""地级市""县级市" | 是 |
| sortKey | string | 排序用的键,通常是拼音 | 是 |
这个字段集是我踩过几次坑之后定下来的。code保证唯一性,pinyin和initial支撑排序和搜索,province解决同名问题,level方便业务做筛选,sortKey是排序时的实际比较字段。多一个字段不多,少一个字段可能就要返工。
3. 中文城市名转拼音的核心技术点
3.1 拼音转换为什么不能用简单的字符映射
中文转拼音这件事,外行觉得就是查表,内行知道坑深得很。最直接的想法是建一个"汉字-拼音"的映射表,逐字转换。但问题在于多音字。城市名里多音字特别常见,比如"重庆"的"重"读chong不读zhong,"厦门"的"厦"读xia不读sha,"蚌埠"的"蚌"读beng不读bang。如果用一个通用的汉字拼音表去转,这些全都会错。
所以城市名的拼音转换,必须使用带词库的拼音库,而不是单字映射。所谓带词库,就是库里预先存了常见词组的正确读音,转换时优先匹配词组。主流的拼音处理库基本都支持自定义词库,你需要做的就是把这些城市名的特殊读音整理成一个词库文件,加载进去。
我整理了一份高频多音字城市清单,这些是必须手动加入词库的:
| 城市名 | 易错读音 | 正确读音 |
|---|---|---|
| 重庆 | zhong qing | chong qing |
| 厦门 | sha men | xia men |
| 蚌埠 | bang bu | beng bu |
| 六安 | liu an | lu an |
| 亳州 | hao zhou | bo zhou |
| 台州 | tai zhou | tai zhou(这个其实没错,但常被误读) |
| 丽水 | li shui | li shui(同理) |
| 铅山 | qian shan | yan shan |
提示:词库不是一次性能建全的。我的做法是先把所有城市转一遍,然后人工抽查前50个和后50个,再随机抽100个,发现错误就补进词库,迭代两三轮基本就干净了。
3.2 拼音库选型与性能考量
选拼音库主要看三个维度:词库支持能力、多音字准确率、运行性能。如果你是在Node.js环境里处理,常见的选择有几种,各有优劣。有的库体积小但词库弱,有的库准确率高但依赖大。我的建议是:如果只是离线生成一次数据,选准确率最高的那个,性能无所谓;如果是要在服务端实时转换,那就要权衡包体积和响应速度。
具体操作上,我通常这样写转换逻辑:
const pinyin = require('pinyin'); // 自定义城市词库,解决多音字 const cityDict = { '重庆': 'chong qing', '厦门': 'xia men', '蚌埠': 'beng bu', // ... 其他特殊城市 }; function getCityPinyin(cityName) { if (cityDict[cityName]) { return cityDict[cityName]; } // 使用拼音库的词组模式转换 const result = pinyin(cityName, { style: pinyin.STYLE_NORMAL, heteronym: false }); return result.map(item => item[0]).join(' '); }这段代码的关键点是heteronym: false,意思是不要输出多音字的所有读音,只输出最可能的那一个。如果设成true,一个"重"字会返回zhong和chong两个,反而增加处理复杂度。
3.3 首字母提取与大小写规范
拿到全拼之后,提取首字母就简单了,取每个拼音音节的首字符,转成大写即可。但这里有个细节:有些城市名带"市"字,有些带"自治州""地区""盟"等后缀。排序的时候,这些后缀要不要参与?
我的处理原则是:排序时去掉行政后缀,展示时保留。比如"北京市"排序时按"bei jing"排,展示时显示"北京市"。这样排出来的顺序才符合人的直觉。如果带着"市"字排,所有城市都以"shi"结尾,虽然不影响首字母排序,但全拼排序时会显得很奇怪。
function getSortKey(cityName) { // 去掉常见行政后缀 const suffixes = ['市', '自治州', '地区', '盟', '自治县', '县']; let name = cityName; for (const suffix of suffixes) { if (name.endsWith(suffix) && name.length > suffix.length) { name = name.slice(0, -suffix.length); break; } } return getCityPinyin(name); }这段逻辑要注意一个边界:如果城市名本身就只有后缀那么长,比如有个地方就叫"市",那不能去掉。所以加了name.length > suffix.length的判断。
4. A-Z排序算法的实现与优化
4.1 按拼音排序 vs 按首字母排序的区别
标题说"按A-Z排序",这里有个歧义需要澄清:是按首字母分组排序,还是按完整拼音排序?两种做法结果不同。
按首字母排序,就是先按第一个字母分组,A组里所有城市排一起,B组排一起,组内再按某种顺序。按完整拼音排序,就是直接比较整个拼音字符串,"北京"(beijing)排在"包头"(baotou)后面,因为b-e大于b-a。
我的建议是:主排序用完整拼音,同时提供首字母分组的能力。因为完整拼音排序的结果是确定的、唯一的,而首字母分组更适合前端做字母导航栏。JSON数据里两个信息都保留,用的时候各取所需。
4.2 排序稳定性和特殊字符处理
JavaScript的Array.prototype.sort在ES2019之后是稳定排序,但如果你用的环境比较老,或者用其他语言处理,就要注意稳定性问题。所谓稳定,就是两个元素比较相等时,保持它们原来的相对顺序。对于城市数据,如果两个城市拼音完全相同(比如同名不同省),稳定排序能保证它们的顺序不会每次生成都不一样。
特殊字符方面,城市名里可能出现的情况不多,但要注意:
- 带"·"的名字,比如某些少数民族地区的地名
- 带数字的,极少但存在
- 纯英文或中英混合的,比如某些特殊区域
处理原则是:排序前统一做归一化,把特殊字符替换成标准字符或者直接剔除,保证排序键是纯字母。
function normalizeSortKey(key) { return key .toLowerCase() .replace(/[^a-z]/g, '') // 只保留字母 .trim(); } const sortedCities = cities.sort((a, b) => { const keyA = normalizeSortKey(a.sortKey); const keyB = normalizeSortKey(b.sortKey); if (keyA < keyB) return -1; if (keyA > keyB) return 1; // 拼音相同时按行政区划代码排,保证稳定性 return a.code.localeCompare(b.code); });4.3 大数据量下的排序性能
全国城市数据,如果把地级市、县级市都算上,大概在六七百条左右。这个量级对任何现代编程语言来说都是毫秒级的事,性能完全不是问题。但如果你还要把乡镇街道也加进来,那就是几万条,这时候排序本身还是很快,瓶颈会在拼音转换上。
优化思路是:拼音转换结果缓存起来。第一次转换后把结果写回数据对象,排序时直接读缓存,不要重复转换。如果数据量真的很大,可以考虑用Map做转换结果的缓存,key是城市名,value是拼音。
const pinyinCache = new Map(); function getCachedPinyin(name) { if (pinyinCache.has(name)) { return pinyinCache.get(name); } const result = getCityPinyin(name); pinyinCache.set(name, result); return result; }这个缓存策略在批量处理时能省下大量重复计算,尤其是数据里有重复城市名的时候。
5. JSON结构设计与输出规范
5.1 扁平结构 vs 嵌套结构的选择
JSON输出有两种主流结构:扁平数组和按省嵌套。扁平数组就是所有城市平铺在一个数组里,每个对象带省份字段。嵌套结构是按省分组,省下面挂城市数组。
两种结构各有适用场景。扁平数组适合做搜索、排序、下拉框数据源,因为遍历简单。嵌套结构适合做级联选择器,省市区联动那种。我的做法是同时输出两份,一份扁平的一份嵌套的,用不同的文件名区分。这样不管什么业务场景都能直接取用,不用再转换。
扁平结构示例:
[ { "code": "110000", "name": "北京市", "pinyin": "bei jing", "initial": "B", "province": "北京市", "level": "直辖市", "sortKey": "beijing" }, { "code": "340100", "name": "合肥市", "pinyin": "he fei", "initial": "H", "province": "安徽省", "level": "地级市", "sortKey": "hefei" } ]嵌套结构示例:
{ "安徽省": { "cities": [ { "code": "340100", "name": "合肥市", "pinyin": "he fei", "initial": "H", "level": "地级市" } ] } }5.2 字段命名规范与兼容性
字段命名看起来是小事,但团队协作时命名不统一会带来很多沟通成本。我的建议是全部用小驼峰,这是前端和后端都最容易接受的风格。如果团队有特殊规范,比如下划线风格,那就统一用下划线,关键是全项目一致。
另外要考虑向后兼容。如果这份数据已经有人在用了,你新增字段没问题,但千万不要改已有字段的名字或类型。比如原来code是字符串,你改成数字,所有依赖它的代码都会挂。所以第一版设计的时候就要想清楚,字段名尽量用通用词汇,避免业务特定词汇。
5.3 输出文件的组织方式
最终交付的文件我一般这样组织:
city-data/ ├── cities-flat.json # 扁平结构,按拼音排序 ├── cities-nested.json # 按省嵌套结构 ├── cities-by-initial.json # 按首字母分组 ├── meta.json # 元信息,包含生成时间、数据版本、条目数 └── README.md # 字段说明和使用示例meta.json这个文件很多人会忽略,但它很重要。里面记录数据是什么时候生成的、基于哪个版本的数据源、总共多少条、有没有已知问题。过几个月回头看,没有这个文件你根本不知道这份数据是什么状态。
{ "version": "1.0.0", "generatedAt": "2024-01-15T10:30:00Z", "totalCount": 682, "source": "行政区划代码表 + 地图服务商数据交叉校验", "knownIssues": [] }6. 实操全流程与关键环节记录
6.1 从零开始生成数据的完整步骤
把前面讲的串起来,整个实操流程分六步。
第一步,准备数据源。把行政区划代码表下载下来,解析成结构化数据。如果是CSV,用对应的解析库读;如果是网页,用解析工具提取表格。这一步的产出是一个包含省、市、县三级记录的原始数组。
第二步,筛选城市层级。从原始数据里挑出所有城市级别的记录。判断标准是行政区划代码的层级位,通常代码的前两位是省,中间两位是市,后两位是县。市级记录的中间两位不为00,后两位为00。直辖市比较特殊,它的市级代码就是省级代码。
第三步,清洗和补全。处理前面提到的边界情况:直辖市、特别行政区、省直辖县级市、同名城市。给每条记录补上省份字段,确保没有遗漏。
第四步,拼音转换。加载自定义词库,对每个城市名做拼音转换,得到全拼和首字母。转换结果写回数据对象。
第五步,排序。按sortKey排序,相同则按code排序保证稳定性。
第六步,输出JSON。按扁平、嵌套、首字母分组三种结构分别输出,同时生成meta信息。
6.2 数据校验的自动化方案
人工检查几百条数据不现实,必须写自动化校验。我通常写这几类校验规则:
- 完整性校验:每个条目必须有code、name、pinyin、initial、province五个字段,缺一不可。
- 唯一性校验:code不能重复,name+province的组合不能重复。
- 格式校验:code必须是6位数字,initial必须是单个大写字母,pinyin只能包含小写字母和空格。
- 一致性校验:initial必须等于pinyin第一个音节的首字母。
- 排序校验:遍历数组,确认每一项的sortKey都不小于前一项。
function validateCities(cities) { const errors = []; const codeSet = new Set(); const nameProvinceSet = new Set(); cities.forEach((city, index) => { // 完整性 ['code', 'name', 'pinyin', 'initial', 'province'].forEach(field => { if (!city[field]) { errors.push(`第${index}条缺少字段${field}`); } }); // 唯一性 if (codeSet.has(city.code)) { errors.push(`code重复:${city.code}`); } codeSet.add(city.code); const npKey = `${city.name}-${city.province}`; if (nameProvinceSet.has(npKey)) { errors.push(`名称+省份重复:${npKey}`); } nameProvinceSet.add(npKey); // 格式 if (!/^\d{6}$/.test(city.code)) { errors.push(`code格式错误:${city.code}`); } if (!/^[A-Z]$/.test(city.initial)) { errors.push(`initial格式错误:${city.initial}`); } // 一致性 const expectedInitial = city.pinyin.charAt(0).toUpperCase(); if (city.initial !== expectedInitial) { errors.push(`initial与pinyin不一致:${city.name}`); } // 排序 if (index > 0) { const prevKey = cities[index - 1].sortKey; if (city.sortKey < prevKey) { errors.push(`排序错误:${city.name} 在 ${cities[index - 1].name} 之后`); } } }); return errors; }这套校验跑一遍,基本能揪出95%以上的问题。剩下的5%是拼音本身的错误,比如多音字没覆盖到,这个只能靠人工抽查。
6.3 版本管理与更新机制
城市数据不是一成不变的,行政区划调整时有发生。所以这份数据需要版本管理。我的做法是用语义化版本号,主版本号在数据结构变化时升,次版本号在新增或删除城市时升,修订号在修正错误时升。
每次更新时,保留一份变更日志,记录这次改了什么。比如"新增某市""修正某市拼音""删除已撤销的某市"。这样使用者能清楚知道两个版本之间的差异,决定要不要升级。
{ "version": "1.1.0", "changelog": [ { "version": "1.1.0", "date": "2024-03-01", "changes": [ "新增:某省某市", "修正:某市拼音由cuo wu改为zheng que" ] } ] }7. 常见问题与排查技巧实录
7.1 拼音转换错误排查表
拼音错误是这份数据最常见的质量问题。我把遇到过的典型错误和排查方法整理成表,方便对照。
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 多音字读错 | 词库未覆盖 | 搜索城市名是否在词库中 | 加入自定义词库 |
| 全拼有空格但首字母错 | 音节切分错误 | 检查拼音库的分词结果 | 调整词库或换库 |
| 拼音带声调 | 输出风格设置错误 | 检查style参数 | 改为STYLE_NORMAL |
| 生僻字无拼音 | 字库缺失 | 单独测试该字 | 手动补充映射 |
| 拼音大小写混乱 | 未统一处理 | 检查是否做了toLowerCase | 统一转小写 |
7.2 排序结果不符合预期的排查思路
排序问题通常有三种表现:顺序完全乱、部分乱、看起来乱其实没错。
顺序完全乱,多半是排序键没设置对,比如用了中文名直接排,或者用了错误的字段。检查sortKey字段的值是不是拼音。
部分乱,通常是多音字导致的,某个城市的拼音错了,自然就排到错误的位置。对照拼音错误排查表处理。
看起来乱其实没错,这种情况常见于用户预期是按首字母分组,但实际是按全拼排。比如"鞍山"(anshan)和"安庆"(anqing),全拼排序是anqing在前,但用户可能觉得都是A开头应该按某种其他规则。这时候要跟用户确认排序规则,或者在文档里写清楚。
7.3 数据使用方的常见疑问
数据交付出去之后,使用方经常会问几个问题,提前在文档里写清楚能省很多沟通。
- 为什么有的城市没有"市"字:因为排序时去掉了后缀,但展示字段保留了。如果使用方需要带后缀的,直接用
name字段。 - 为什么直辖市在省份字段里也是自己:因为直辖市省级和市级是同一个,这样设计保证每条记录都有省份字段,不会出现空值。
- 为什么有两个城市拼音一样:同名不同省,用
code或province区分。 - 数据多久更新一次:看行政区划调整频率,一般半年到一年检查一次。
提示:文档里最好附一个最小可运行的使用示例,比如怎么在前端根据首字母分组渲染一个城市选择器。使用方拿到就能跑,比看文字说明快得多。
8. 数据质量保障的独家经验
8.1 交叉验证的实操方法
单一数据源不可靠,这是数据工程的基本常识。我的做法是至少用两个独立来源做交叉验证。具体操作是:把两个来源的城市列表都提取出来,做集合运算。交集部分基本可信,差集部分逐条人工确认。
差集里常见的情况有:一个来源有但另一个没有的,可能是新增城市或者已撤销城市;两个来源都有但拼写不同的,可能是别名或者错误。这些都要查权威资料确认。
交叉验证能发现的问题类型:
- 遗漏的城市(一个源有另一个源没有)
- 多余的城市(已撤销但未清理)
- 名称错误(别名、错别字)
- 层级错误(县级市被当成地级市)
8.2 人工抽查的策略
自动化校验跑完之后,人工抽查是最后一道防线。但几百条数据不可能全看,要讲究策略。
我的抽查策略是分层抽样加重点覆盖。分层抽样是按省份分层,每个省抽2-3个城市,保证覆盖全国。重点覆盖是专门检查那些容易出错的城市:多音字城市、生僻字城市、同名城市、新设立的城市。这两部分加起来大概抽100条左右,能覆盖大部分风险点。
抽查的时候不要只看拼音对不对,还要看排序位置对不对。有时候拼音对了但排序位置不对,说明排序逻辑有问题。
8.3 持续维护的机制设计
数据交付不是终点,而是起点。要建立持续维护机制,才能保证数据长期可用。
我的做法是:建一个issue跟踪列表,使用方发现任何问题都可以提,定期集中处理。同时订阅行政区划调整的公开信息,有变化就触发更新。每次更新都走一遍完整的校验流程,确保新数据不引入新问题。
维护机制里最重要的是变更记录。每次改了什么、为什么改、谁改的,都要记清楚。过一年回头看,没有变更记录根本不知道数据是怎么演变成现在这样的。
9. 从城市数据延伸的更多可能
9.1 基于城市数据的搜索优化
有了拼音和首字母,城市搜索就很好做了。用户输入"bj",能匹配到"北京";输入"beijing",也能匹配到;输入"北",还是能匹配到。实现思路是建一个多字段索引,name、pinyin、initial都作为搜索字段。
function searchCity(keyword, cities) { const kw = keyword.toLowerCase(); return cities.filter(city => city.name.includes(keyword) || city.pinyin.replace(/\s/g, '').startsWith(kw) || city.initial === keyword.toUpperCase() ); }这个搜索函数支持中文、全拼、首字母三种输入方式,基本覆盖了用户的所有输入习惯。如果要更高级的模糊匹配,可以引入编辑距离算法,但城市数据量小,简单的前缀匹配已经够用。
9.2 城市数据的可视化应用
城市数据配上经纬度,就能做可视化。比如按省份着色渲染地图,按城市等级调整点的大小,按首字母做聚合统计。这些应用都建立在结构化数据的基础上,数据质量直接决定可视化效果。
如果要做地域分布分析,还可以把城市数据和业务数据关联。比如用户分布、订单分布、物流覆盖,都是以城市为维度聚合的。这时候城市数据的准确性和完整性就特别重要,一个城市名错了,对应的业务数据就聚合不到正确的位置。
9.3 多语言场景下的城市数据
如果业务涉及多语言,城市数据还需要英文名或其他语言名称。这时候JSON结构要扩展,加一个names对象,里面按语言代码存不同语言的名称。
{ "code": "110000", "name": "北京市", "names": { "zh": "北京市", "en": "Beijing", "ja": "北京" }, "pinyin": "bei jing", "initial": "B" }多语言数据的采集比中文名麻烦得多,因为不同语言对同一城市的译名可能不统一。我的建议是优先使用权威的官方译名,没有官方译名的用通用译名,并在文档里注明来源。
10. 我在这个项目里踩过的坑
最后分享几个我实际踩过的坑,都是文档里不会写但实际会遇到的。
第一个坑是拼音库的版本差异。同一个库的不同版本,对同一个多音字的处理可能不一样。我遇到过升级库之后,某些城市的拼音变了,导致排序结果和之前不一致。所以升级拼音库之后一定要重新跑一遍校验,对比新旧结果。
第二个坑是行政区划代码的变更。有些城市撤市设区之后,代码会变。如果数据里还保留着旧代码,和外部系统对接时就会对不上。所以每次更新都要检查代码变更,及时同步。
第三个坑是JSON的编码问题。中文在JSON里默认是Unicode转义,虽然功能上没问题,但可读性差。输出的时候要设置ensure_ascii=False,让中文直接显示。这个在Python里是json.dumps(data, ensure_ascii=False),在JavaScript里默认就是直接输出中文,不用特殊处理。
第四个坑是文件体积。如果字段设计得太冗余,几百条数据也能撑到几百KB。对于前端使用来说,这个体积有点大。优化方法是精简字段名、去掉不必要的空格、考虑用数组代替对象存储。但要注意,优化不能牺牲可读性和可维护性,毕竟这份数据是要给人看的。
第五个坑是排序规则的沟通成本。我一开始没在文档里写清楚排序规则,结果使用方以为排序错了,来回沟通了好几次。后来在文档里明确写了"按拼音全拼排序,相同则按行政区划代码排序",就再也没人问了。所以文档一定要写清楚规则,不要假设使用方知道你的设计意图。
这份城市JSON数据看起来简单,但要做好做精,涉及的细节一点不少。从数据源选择、拼音转换、排序算法到JSON结构设计,每一步都有讲究。希望这篇内容能帮你少走弯路,直接产出一份能用的数据。如果你在实际操作中遇到其他问题,欢迎交流,我也还在持续维护和优化这份数据。