将3D JSON对象转换为Bash中的"伪3D"关联数组
如何将JSON船舶数据转换为Bash的"伪3D"关联数组
需求说明
我需要把给定结构的JSON数据转换成Bash中的"伪3D"关联数组,格式示例如下:
VESSELS[$mmsi:mmsi]=367513050 VESSELS[$mmsi:lat]=42.380329 ...
核心要求是用mmsi值+对象键作为关联数组的索引。
最终目标:
- 声明一个关联数组
- 通过curl从URL获取JSON文件
- 用JSON中的键值对更新关联数组(键格式为
VESSELS[$mmsi:$key]),同时保留数组中未被提及的原有元素不变
现有实现的问题
我目前的实现依赖一个不可靠的假设:mmsi字段是每个JSON对象的首个字段。现有代码会生成三个变量:存储所有mmsi的VESSEL_INDEX数组、存储所有键的KEY_INDEX数组,以及可通过VESSELS[$mmsi:$key]访问的VESSELS关联数组。
现有代码:
declare -A VESSELS declare -a VESSEL_INDEX declare -a KEY_INDEX while read -r keyvalue do key="${keyvalue%%=*}" value="${keyvalue#*=}" if [[ "$key" == "mmsi" ]] then mmsi="$value" [[ -n "$mmsi" ]] && VESSEL_INDEX+=("$mmsi") fi [[ ! " ${KEY_INDEX[*]} " =~ " ${key} " ]] && KEY_INDEX+=("${key}") [[ -n "$mmsi" ]] && VESSELS["$mmsi:$key"]="$value" done <<< "$(curl -sL "$AIS_URL/ships_full.json" | jq -r ".ships[]|to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")"
输入的JSON示例
{ "count": 2, "ships": [ { "mmsi": 367513050, "lat": 42.380329, "lon": -71.042946, "distance": 0.317654, "mmsi_type": 1, "level": -28.161266, "count": 6895, "ppm": 4.918982, "heading": null, "cog": null, "speed": 0, "to_bow": 10, "to_stern": 17, "to_starboard": 3, "to_port": 5, "shiptype": 52, "msg_type": 0, "country": "US", "status": 0, "callsign": "WDG2188", "shipname": "VINCENT D. TIBBETTS", "destination": "BOSTON", "last_signal": 0 }, { "mmsi": 367447520, "lat": 42.324032, "lon": -70.994347, "distance": 3.777312, "mmsi_type": 1, "level": -37.657475, "count": 1103, "ppm": -1.157407, "heading": 156, "cog": 155.5, "speed": 28.4, "to_bow": 24, "to_stern": 20, "to_starboard": 9, "to_port": 4, "shiptype": 40, "msg_type": 0, "country": "US", "status": 0, "callsign": "WDF4062", "shipname": "SALACIA", "destination": "XX XXX>?? ???", "last_signal": 0 } ], "error": false }
改进方案
要解决依赖mmsi字段顺序的问题,我们可以让jq先提取每个船舶对象的mmsi,再把mmsi和每个键值对组合输出,这样不管mmsi在JSON中的位置如何,都能正确获取到。
改进后的代码:
declare -A VESSELS declare -a VESSEL_INDEX declare -a KEY_INDEX # 用jq先获取mmsi,再遍历所有键值对输出"mmsi:key=value"格式的行 while IFS='=' read -r index value do # 拆分index为mmsi和key mmsi="${index%%:*}" key="${index#*:}" # 更新VESSEL_INDEX(去重) if [[ ! " ${VESSEL_INDEX[*]} " =~ " ${mmsi} " ]]; then VESSEL_INDEX+=("$mmsi") fi # 更新KEY_INDEX(去重) if [[ ! " ${KEY_INDEX[*]} " =~ " ${key} " ]]; then KEY_INDEX+=("$key") fi # 更新关联数组 VESSELS["$index"]="$value" done <<< "$(curl -sL "$AIS_URL/ships_full.json" | jq -r '.ships[] | .mmsi as $mmsi | to_entries[] | "\($mmsi):\(.key)=\(.value|tostring)"')"
方案优势
- 不再依赖mmsi在JSON对象中的位置,不管mmsi是第几个字段都能正确处理
- jq直接输出
mmsi:key=value格式的行,避免了在bash循环中临时存储mmsi的逻辑,减少出错概率 - 保留了原有需求中的
VESSEL_INDEX和KEY_INDEX数组,同时正确更新VESSELS关联数组 - 自动保留数组中未被JSON提及的原有元素(因为只更新JSON中存在的键值对,不会清空整个数组)
内容的提问来源于stack exchange,提问作者kx1t
相关产品推荐
相关产品推荐

