news 2026/10/16 6:48:16

全球省市数据JSON处理:结构体检、转换SQL与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
全球省市数据JSON处理:结构体检、转换SQL与避坑指南

简介:《全球各国省市数据.json》是一份面向地理信息开发与数据分析人群的全球行政区划数据集。数据采用JSON格式组织,以“国家-省份-城市”三层结构存储,并为每个节点提供唯一ID,便于在地图应用、区域统计、位置服务等场景中快速定位与检索。压缩包内仅含1个JSON文件,整体大小约82KB,结构精简、解析成本低,适合直接嵌入项目或作为教学示例使用。值得一提的是,资源中省市节点ID唯一,可据此进行关联查询;对于没有下属市分类的省份,数据直接用省名作为市名,使用前需留意这一规则。该资源目前在CSDN已有1595人学习,对需要全球地理基础数据、行政区划字典或前端联动组件的开发者来说,是一份实用的小型数据支撑。

1. 这份“全球各国省市数据.json”到底能省下你多少事

做海外业务、搭跨境电商站点,或者做地图聚合分析的时候,第一步往往不是写代码,而是找一套能用的“全球各国省市、城市名”基础数据。我见过不少人在这上面耗掉一两天:要么找到的是旧版的二维表,要么只有国家没有省市,要么省市英文名和中文名对不上。这份全球各国省市数据.json解决的就是这个场景——把国家、省份、城市按层级放在一个 JSON 文件里,拿到手就能直接用于级联选择器、行政区域维表导入、或者清洗订单地址字段。先说结论:它不解决“数据源有没有”的问题,但能解决“数据能不能直接用”的问题;适合后端、前端、数据分析从业者拿来当底座,而不是当展示 Demo。

真正上手后你会发现,这类数据最大的坑不在结构,而在“口径一致性”:有的国家行政区划叫省,有的叫州,有的叫大区,甚至同一国家内县市级归属逻辑都不一样。所以这篇笔记我会从数据结构拆起,给你体检脚本和转换脚本,再把我踩过的坑按现象、原因、解决顺序列清楚。

2. 拆开 JSON 看结构:层级、字段与合法性体检

先别急着写业务代码,拿到任何一份数据资源,第一步都是把它的“骨架”摸清楚。这份 JSON 从命名上看是典型的层级嵌套结构,但我建议你亲手验证一遍,因为网上流传的同类文件经常存在“国家下有省份、省份下没城市”的半成品情况。

2.1 预期的三层嵌套结构与常见变体

我处理的这类全球行政区域数据,通常有两种主流组织方式:第一种是嵌套数组,根节点是国家列表,每个国家内携带provinces数组,每个省份内再携带cities数组;第二种是扁平列表,每条记录用parent_code指向父级。这份文件从标题和常见分发形式看,大概率是第一种。嵌套结构的好处是读取直观、做级联菜单时无需递归查父节点;代价是重复字段多,比如国家名会在每个省份节点里重复出现。如果你拿到的变体是扁平结构,后面我给的转换脚本也能适配,只需要多一步按parent_code分组的逻辑。

2.2 关键字段口径说明

不管具体字段名怎么起,这类数据都必须包含几个核心要素:国家唯一标识(通常是国家代码)、国家名称(中英文或本地语言)、省份名称、城市名称。你可能还会看到iso_code、country_code、admin_level这类附加字段。country_code的取值方式直接影响后续做地图可视化时能不能和地图库对上,常见的地图库更认 ISO 3166-1 alpha-2,如果你的数据里给的是三位数字码,最好先做映射转换。省份和城市字段如果没有代码只有名称,在数据清洗场景下问题不大,但如果要跨系统对接,建议自己补充拼音或统一编码。

2.3 数据体检脚本:先跑一遍再决定怎么用

拿到文件我做的第一件事不是打开看,而是写一个体检脚本,统计各层级的数量、缺失情况。下面是适合大多数 JSON 行政数据的小脚本,保存为check_admin_data.py运行。

import json from collections import Counter def inspect_data(file_path, encoding="utf-8"): with open(file_path, "r", encoding=encoding) as f: data = json.load(f) # 兼容两种常见结构:列表嵌套 / 字典包裹 if isinstance(data, dict): countries = data.get("countries", data.get("data", [])) else: countries = data print("国家总数:", len(countries)) total_provinces = 0 total_cities = 0 missing_province_countries = [] missing_city_provinces = [] level_counter = Counter() name_keys = set() for country in countries: if not isinstance(country, dict): continue name_keys.update(country.keys()) provinces = country.get("provinces", country.get("states", [])) if not provinces: missing_province_countries.append(country.get("name", country.get("code", "未知"))) total_provinces += len(provinces) for province in provinces: cities = province.get("cities", province.get("cities_list", [])) if not cities: missing_city_provinces.append(province.get("name", "未知省份")) total_cities += len(cities) level_counter[province.get("admin_level", "未标注")] += 1 print("省份总数:", total_provinces) print("城市总数:", total_cities) print("缺少省份的国家:", missing_province_countries[:5]) print("缺少城市的省份示例:", missing_city_provinces[:10]) print("admin_level 取值分布:", level_counter.most_common(5)) print("顶层字段名:", name_keys) if __name__ == "__main__": inspect_data("global_admin_data.json")

这个脚本的逻辑分四步:先按文件内容形态兼容“字典包裹”和“直接列表”两种加载方式;再遍历国家节点,统计国家数;然后下钻到省份和城市,逐层统计数量;最后把缺失省份的国家和缺失城市的省份单独记录。level_counter用来观察admin_level字段是否被正确填充,如果大量省份没有标注行政级别,说明这份数据的层级信息不完整,转 SQL 时你需要自己定省和市的边界。代码里的name_keys用来打印顶层字段名,方便你确认实际字段是provinces还是states,不同来源命名差异很大。

参数层面需要特别留意的两个点:encoding默认是utf-8,如果 Windows 下用记事本另存过文件,可能要改成utf-8-sig才能避开首字符BOM导致的 key 读取异常;inspect_data函数返回前打印的“缺少省份的国家”列表,是判断这份数据完整度的第一手依据,如果超过 10 个国家缺失,后面做全球功能时就要准备兜底数据。

3. 让它跑起来:转 SQL 维表与前端级联数据的标准做法

体检通过后,下一步就是把 JSON 变成能落库、能渲染的业务数据。这里有两种最常见的使用路径:后端建行政区域维表,以及前端直接用裁剪后的 JSON 做级联选择器。两条路我都给你可直接抄的转换脚本。

3.1 后端场景:把嵌套 JSON 转成三张关系表

关系型数据库里最稳的设计是“国家表、省份表、城市表”三张表分贝存储,用自增主键关联。这种设计在订单地址清洗、物流区域筛选、报表统计时都能少写很多CASE WHEN。

import json import csv def convert_to_relational(file_path, output_prefix="admin"): with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) # 兼容 dict 包裹的格式 countries = data.get("countries", data) if isinstance(data, dict) else data country_rows = [] province_rows = [] city_rows = [] country_id = 1 province_id = 1 city_id = 1 for country in countries: country_code = country.get("code", country.get("country_code", "")) country_name = country.get("name", country.get("country_name", "")) country_rows.append([country_id, country_code, country_name]) provinces = country.get("provinces", country.get("states", [])) for province in provinces: province_name = province.get("name", "") province_rows.append([province_id, country_id, province_name]) cities = province.get("cities", []) for city in cities: city_name = city.get("name", "") city_rows.append([city_id, province_id, city_name]) city_id += 1 province_id += 1 country_id += 1 with open(f"{output_prefix}_countries.csv", "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow(["id", "code", "name"]) writer.writerows(country_rows) with open(f"{output_prefix}_provinces.csv", "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow(["id", "country_id", "name"]) writer.writerows(province_rows) with open(f"{output_prefix}_cities.csv", "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow(["id", "province_id", "name"]) writer.writerows(city_rows) if __name__ == "__main__": convert_to_relational("global_admin_data.json")

这个脚本的核心逻辑是:用三个计数器分别维护三张表的主键,通过嵌套循环把 JSON 的层级关系转成外键关联。省份所属的国家 ID、城市所属的省份 ID 都通过当前循环所在层级的计数值确定。注意脚本里对“国家下没有省份”的情况没有做特殊处理,因为体检阶段就应该发现并补齐了,如果你不想补数据,至少应在省份表里保留一个“未知省份”占位,否则订单地址里的城市名会丢失层级关系。

3.2 前端场景:生成轻量级级联选择器数据

如果直接给前端丢一个几百兆的完整 JSON,首屏加载会非常吃力。常见做法是拆分出“按国家裁剪的轻量子集”,只保留前端级联菜单需要的字段。

const fs = require("fs"); const raw = JSON.parse(fs.readFileSync("global_admin_data.json", "utf-8")); const countries = raw.countries || raw; const slimData = countries.map((country) => ({ code: country.code || country.country_code, name: country.name || country.country_name, provinces: (country.provinces || country.states || []).map((province) => ({ name: province.name, cities: (province.cities || []).map((city) => city.name), })), })); fs.writeFileSync( "global_admin_slim.json", JSON.stringify(slimData), "utf-8" );

脚本做的事情很简单:过滤掉id、admin_level等前端用不到的字段,只保留“编码、国家名、省份名、城市名”四级。这样裁剪后体积能减少 60% 左右。如果你做的是国家选择器而不是完整地址联动,还可以进一步裁剪,只保留国家和省份两级,城市按需懒加载——把slimData按国家拆分,前端选中某个国家后再请求对应的省份城市子文件,能显著减少首包体积。

3.3 前后端联调时的数据协议约定

转完结构后,最容易出的问题不是数据错,而是前后端对层级字段的命名约定不一致。我一般会在接口文档里固定这样一层约定:国家节点的code字段必须与 ISO 标准对齐,省份和城市只传name,所有层级用数组下标做父子关系。这份 JSON 里如果省份或城市带code字段,建议保留并传值,因为做编辑回显时,只靠名称很容易被同名城市搞混。

4. 常见问题与避坑:五个翻车点与排查顺序

这类数据资源表面上是一份 JSON,实际用起来翻车点集中在编码、层级口径、特殊行政区域和文件体积四个方面。下面是我按“现象 → 原因 → 解决”整理的五个高频问题,按排查优先级排序。

4.1 JSON 读取乱码

现象:用json.load()读文件后,中文城市名变成了中åÂ之类的乱码串;或者用 Node 的fs.readFileSync读取后 JSON.parse 直接报错。原因:文件本身是 UTF-8 编码,但你的代码运行环境默认用了 GBK,或者文件被 Windows 记事本另存为ANSI格式。解决:读取时明确指定编码,Python 里open(file, encoding="utf-8"),如果文件开头有 BOM 就改用utf-8-sig。拿到任何 JSON 数据文件,我每次第一件事就是用十六进制编辑器看文件头几字节,EF BB BF就是 BOM 存在的铁证。

4.2 省、州、大区口径不一致

现象:转换到 SQL 后,发现某些国家的省份数量异常多,或者城市表里混入了“州”级别数据。原因:这个 JSON 里的provinces字段对不同国家可能混用“省、州、大区、邦”等不同行政层级,比如案例国家 A 的最高层级是大区,案例国家 B 是州。解决:不要试图统一各国行政区划,保留数据的原生层级,只把admin_level字段在转换时映射成数值级别;如果字段缺失,按“该国家最小行政单元”来归入城市表。常见做法是在国家代码后面加后缀来区分,如BR-SP和CN-11就不会混。

4.3 特殊行政归属的数据歧义

现象:某些地区的归属在数据里有两种写法,同一地区既出现在一个国家的省份列表下,又出现在另一个国家的城市列表里。原因:许多行政数据源在收录时存在归属争议或历史遗留问题,数据维护者可能只做了合并处理没去重。解决:用脚本对全量数据做一次“名称-层级”重复扫描,发现同名字段同时出现在两个层级时,保留层级更深的那条记录;处理不了的人工确认,不要假设数据源是对的。

4.4 中英文覆盖不完整

现象:做搜索功能时输入中文城市名搜不到,但英文能搜到;或者反过来,中文界面能显示但英文界面是空字符串。原因:这份 JSON 的name字段大概率以某一种语言为主,没有完整的中英双语对照。解决:前端项目接入时,准备好“中文名 + 别名 + 英文名”三个字段映射关系;如果这份数据里没有别名字段,建议直接用城市拼音和中文名做模糊匹配兜底。后端清洗订单地址时,同理不要强制要求按中文名匹配,优先用国家、城市代码匹配,名称只做展示。

4.5 文件体积大导致内存占用过高

现象:在后端服务里把整份 JSONjson.load()后,服务内存涨了几百兆;或者前端加载后页面明显卡顿。原因:全量数据是世界级的,嵌套冗余字段多,单个国家的大省份动辄上千城市,全量对象常驻内存不现实。解决:后端改用流式解析逐国家处理,或者按国家拆分成多个小文件;前端用裁剪脚本生成 slim 版,并开启 gzip 压缩传输。我一般会在生产环境再套一层 Redis 缓存,把国家列表和省份列表拆成热 key 保存,避免每次请求都穿透到 JSON 解析。

5. 进阶技巧:给全量数据做关键词检索增强

行政数据直接展示没问题,但一旦涉及“输入城市名联想”,你会发现“仅靠name字段匹配”根本不够用。这个进阶技巧的目的很明确:在不引入外部搜索引擎的前提下,用一份简单的索引文件解决“中文名、拼音、英文名”混合搜索的问题。

方案分三步:先从 JSON 提取全量国家、省份、城市名,生成带拼音和首字母的扁平索引;然后在前端离线做关键字匹配;最后后端对未命中的输入做一次兜底全表扫描。生成索引的脚本如下。

import json def build_search_index(file_path, output_path): with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) countries = data.get("countries", data) if isinstance(data, dict) else data index_entries = [] for country in countries: country_name = country.get("name", "") cc = country.get("code", "") index_entries.append({"type": "country", "name": country_name, "parent": "", "code": cc}) for province in country.get("provinces", []): province_name = province.get("name", "") index_entries.append({"type": "province", "name": province_name, "parent": country_name, "code": cc}) for city in province.get("cities", []): index_entries.append({"type": "city", "name": city.get("name", ""), "parent": province_name, "code": cc}) # 生成简名字段 for entry in index_entries: entry["simple_name"] = entry["name"].replace("省", "").replace("市", "").replace("州", "") with open(output_path, "w", encoding="utf-8") as f: json.dump(index_entries, f, ensure_ascii=False, indent=1) if __name__ == "__main__": build_search_index("global_admin_data.json", "search_index.json")

脚本里比较关键的设计是simple_name字段:去掉“省、市、州”这类行政后缀,让“湖南”和“湖南省”输入时都能命中同一条记录。parent字段保留了父子上下文,这样搜索“朝阳区”时你能立刻知道它属于哪座城市,前端展示结果时可以带上“北京市 朝阳区”这样的层级前缀。如果你需要更精细的拼音索引,可以在这个索引生成后追加一列拼音全拼和首字母,再对这三列做一次多字段匹配。

从那以后我每次接入新的行政区域数据,都会强制走一遍“体检 → 裁剪 → 建索引”的流程:先跑一遍统计脚本看缺失,再决定要不要补数据,然后才谈转表和渲染。这套流程帮我避免过好几次上线前一晚才发现数据缺层级的窘境,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 6:46:44

JavaWeb学生成绩管理系统:Servlet+JSP+JDBC三层架构实战解析

简介:面向JavaWeb学习者与课程设计人群,这是一份学生成绩管理系统完整项目,基于Servlet、JSP、JSTL、EL等技术栈,配合Eclipse与SQL Server实现成绩录入、查询、统计分析和报表生成,适合毕业设计、课设或项目实践参考。…

作者头像 李华
网站建设 2026/10/16 6:46:28

QEMU+Multiboot手写最小操作系统内核:从汇编入口到屏幕输出

简介:面向操作系统初学者的启动实验资源,围绕QEMU模拟器完成操作系统引导,适配正在做操作系统课程实验的读者。压缩包内提供实验报告和完整源代码,通过编写多引导协议(Multiboot)头部,在VGA显存…

作者头像 李华
网站建设 2026/10/16 6:46:07

OpenClaw 与飞书机器人联调:本地私有 AI 数字同事部署全指南

最近我把个人 AI 助手的交互界面从网页对话框搬到了飞书里,后端跑的是 Mac 本地部署的 OpenClaw。现在每天早上九点,飞书群里会自动出现一份当天待办简报;下午在手机上随手发一条消息,它就能帮我查资料、整理会议纪要、执行定时脚…

作者头像 李华
网站建设 2026/10/16 6:45:37

JavaWeb同学录系统课设:三层架构部署与排错指南

简介:这是一份面向Java Web课程设计的“同学录系统”期末项目源码包,采用前后端结合的典型Web应用,解决成员信息记录与管理问题,适合学习Servlet/JSP或准备课程设计的开发者参考。压缩包约1021KB,共48个文件&#xff0…

作者头像 李华
网站建设 2026/10/16 6:43:42

C#串口通信解析Zigbee二进制帧的完整实践指南

简介:这是一套面向物联网开发初学者与嵌入式上位机开发者的学习实践资源,聚焦C#桌面应用与Zigbee无线传感网络的串口通信集成,解决传感器数据实时采集、可视化显示及远程控制等典型工业监控场景需求。资源包共87个文件,含11个核心…

作者头像 李华
网站建设 2026/10/16 6:43:35

电力纵向加密认证装置现场配置与排障实战

简介:科东纵向加密客户端V1.5是与PSTunnel2000加密装置配套使用的软件资源,定位为电力系统纵向边界安全通信组件,主要面向电网信息运维人员、网络安全工程师以及关键信息基础设施安全管理人员。它针对电力调度数据网跨地域、跨层级传输中的敏…

作者头像 李华