当前位置: 首页 > article >正文

告别文档踩坑:手把手教你用OkHttp和Gson解析OneNET API返回的复杂JSON数据

告别文档踩坑手把手教你用OkHttp和Gson解析OneNET API返回的复杂JSON数据在Android开发中处理网络请求和JSON数据解析是每个开发者都必须掌握的基本技能。然而当面对像OneNET这样的物联网平台返回的复杂嵌套JSON结构时即使是经验丰富的开发者也可能会遇到各种问题。本文将带你深入理解如何高效、安全地解析OneNET API返回的JSON数据避免常见的陷阱和错误。1. 理解OneNET API返回的数据结构OneNET平台作为国内领先的物联网开放平台其API返回的数据结构通常具有以下特点多层嵌套数据通常包含多个层级如根对象、数据数组、数组中的对象等动态字段某些字段可能根据设备类型不同而有所变化混合类型同一个字段在不同情况下可能返回不同类型的数据典型的OneNET API响应如下所示{ code: 0, data: [ { identifier: temperature, time: 1700127636130, value: 25.5, data_type: float, access_mode: 读写 }, { identifier: humidity, time: 1700127636130, value: 65, data_type: int, access_mode: 读写 } ], msg: success, request_id: f97096fb25a94f2884a043c510821485 }2. 设计合理的Java Bean类使用Gson解析JSON的第一步是创建对应的Java类结构。对于上述JSON结构我们需要设计两个主要类2.1 根对象类public class OneNetResponse { private int code; private ListDeviceData data; private String msg; private String requestId; // Getters and setters public int getCode() { return code; } public void setCode(int code) { this.code code; } public ListDeviceData getData() { return data; } public void setData(ListDeviceData data) { this.data data; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg msg; } public String getRequestId() { return requestId; } public void setRequestId(String requestId) { this.requestId requestId; } }2.2 数据对象类public class DeviceData { private String identifier; private long time; private String value; private String dataType; private String accessMode; private String name; private String description; // Getters and setters public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier identifier; } public long getTime() { return time; } public void setTime(long time) { this.time time; } public String getValue() { return value; } public void setValue(String value) { this.value value; } public String getDataType() { return dataType; } public void setDataType(String dataType) { this.dataType dataType; } public String getAccessMode() { return accessMode; } public void setAccessMode(String accessMode) { this.accessMode accessMode; } public String getName() { return name; } public void setName(String name) { this.name name; } public String getDescription() { return description; } public void setDescription(String description) { this.description description; } }注意在实际项目中建议使用Lombok的Data注解来简化getter和setter的编写但需要确保项目已正确配置Lombok插件。3. 使用OkHttp发起请求并处理响应3.1 添加依赖首先确保在build.gradle中添加必要的依赖dependencies { implementation com.squareup.okhttp3:okhttp:4.9.3 implementation com.google.code.gson:gson:2.8.9 }3.2 发起GET请求public class OneNetApiClient { private static final String BASE_URL http://iot-api.heclouds.com; private final OkHttpClient client; private final Gson gson; public OneNetApiClient() { this.client new OkHttpClient(); this.gson new Gson(); } public void fetchDeviceData(String productId, String deviceName, String token, Callback callback) { String url String.format(%s/thingmodel/query-device-property?product_id%sdevice_name%s, BASE_URL, productId, deviceName); Request request new Request.Builder() .url(url) .header(Authorization, token) .build(); client.newCall(request).enqueue(callback); } }3.3 处理响应数据public class MainActivity extends AppCompatActivity { private TextView dataDisplay; private OneNetApiClient apiClient; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dataDisplay findViewById(R.id.data_display); apiClient new OneNetApiClient(); String token your_generated_token; String productId your_product_id; String deviceName your_device_name; apiClient.fetchDeviceData(productId, deviceName, token, new Callback() { Override public void onFailure(Call call, IOException e) { runOnUiThread(() - Toast.makeText(MainActivity.this, 请求失败: e.getMessage(), Toast.LENGTH_SHORT).show()); } Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { runOnUiThread(() - Toast.makeText(MainActivity.this, 请求失败: response.code(), Toast.LENGTH_SHORT).show()); return; } String responseData response.body().string(); OneNetResponse oneNetResponse apiClient.getGson().fromJson(responseData, OneNetResponse.class); if (oneNetResponse.getCode() ! 0) { runOnUiThread(() - Toast.makeText(MainActivity.this, API错误: oneNetResponse.getMsg(), Toast.LENGTH_SHORT).show()); return; } StringBuilder displayText new StringBuilder(); for (DeviceData data : oneNetResponse.getData()) { displayText.append(String.format(%s: %s\n, data.getName() ! null ? data.getName() : data.getIdentifier(), data.getValue())); } runOnUiThread(() - dataDisplay.setText(displayText.toString())); } }); } }4. 处理常见问题和边界情况4.1 空指针异常防护在处理JSON数据时空指针异常是最常见的问题之一。我们可以采取以下防护措施使用Nullable和NonNull注解明确标记可能为null的字段为可能为null的字段提供默认值使用Optional类包装可能为null的值public class DeviceData { // ...其他字段 Nullable private String description; public String getSafeDescription() { return description ! null ? description : 无描述; } }4.2 类型转换处理OneNET API返回的value字段可能是多种类型我们可以通过以下方式处理public Object getTypedValue() { if (dataType null || value null) { return null; } switch (dataType.toLowerCase()) { case float: try { return Float.parseFloat(value); } catch (NumberFormatException e) { return null; } case int: case int32: try { return Integer.parseInt(value); } catch (NumberFormatException e) { return null; } case bool: return true.equalsIgnoreCase(value); default: return value; } }4.3 使用TypeAdapter处理复杂情况对于特别复杂的JSON结构可以自定义Gson的TypeAdapterpublic class DeviceDataAdapter extends TypeAdapterDeviceData { Override public void write(JsonWriter out, DeviceData value) throws IOException { // 序列化逻辑 } Override public DeviceData read(JsonReader in) throws IOException { DeviceData data new DeviceData(); in.beginObject(); while (in.hasNext()) { String name in.nextName(); switch (name) { case identifier: data.setIdentifier(in.nextString()); break; case time: data.setTime(in.nextLong()); break; // 其他字段处理... } } in.endObject(); return data; } }然后在使用Gson时注册这个适配器Gson gson new GsonBuilder() .registerTypeAdapter(DeviceData.class, new DeviceDataAdapter()) .create();5. 性能优化和最佳实践5.1 使用缓存减少解析开销对于频繁请求的相同数据结构可以考虑缓存解析结果private MapString, OneNetResponse responseCache new ConcurrentHashMap(); public OneNetResponse parseResponse(String json) { String cacheKey Integer.toHexString(json.hashCode()); if (responseCache.containsKey(cacheKey)) { return responseCache.get(cacheKey); } OneNetResponse response gson.fromJson(json, OneNetResponse.class); responseCache.put(cacheKey, response); return response; }5.2 批量处理数据更新如果设备数据更新频繁可以考虑批量处理而不是每次更新都刷新UIprivate Handler batchHandler new Handler(Looper.getMainLooper()); private Runnable batchUpdateRunnable; private ListDeviceData pendingUpdates new ArrayList(); private void scheduleBatchUpdate(DeviceData newData) { pendingUpdates.add(newData); if (batchUpdateRunnable ! null) { batchHandler.removeCallbacks(batchUpdateRunnable); } batchUpdateRunnable () - { // 处理所有待更新数据 updateUIWithData(pendingUpdates); pendingUpdates.clear(); }; batchHandler.postDelayed(batchUpdateRunnable, 500); // 500ms批处理窗口 }5.3 使用Kotlin协程简化异步代码如果项目使用Kotlin可以大幅简化异步处理代码suspend fun fetchDeviceDataSuspend(productId: String, deviceName: String, token: String): OneNetResponse { return withContext(Dispatchers.IO) { val url $BASE_URL/thingmodel/query-device-property?product_id$productIddevice_name$deviceName val request Request.Builder() .url(url) .header(Authorization, token) .build() val response client.newCall(request).execute() if (!response.isSuccessful) { throw IOException(请求失败: ${response.code}) } gson.fromJson(response.body?.string(), OneNetResponse::class.java) } }6. 安全注意事项6.1 鉴权信息的安全存储永远不要将鉴权信息硬编码在代码中应该使用Android的安全存储机制// 使用AndroidX的安全库存储敏感信息 private fun saveToken(token: String) { val masterKey MasterKey.Builder(applicationContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() val sharedPreferences EncryptedSharedPreferences.create( applicationContext, secure_prefs, masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) with(sharedPreferences.edit()) { putString(onenet_token, token) apply() } }6.2 网络通信安全确保所有通信都使用HTTPS并配置OkHttp的安全策略private fun createSecureOkHttpClient(): OkHttpClient { val trustManager createTrustManager() // 自定义信任管理器 return OkHttpClient.Builder() .sslSocketFactory(trustManager.socketFactory, trustManager) .hostnameVerifier { hostname, session - // 自定义主机名验证逻辑 HttpsURLConnection.getDefaultHostnameVerifier() .verify(iot-api.heclouds.com, session) } .build() }7. 调试和问题排查技巧7.1 使用拦截器记录网络请求添加OkHttp的日志拦截器可以帮助调试class LoggingInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request chain.request() val t1 System.nanoTime() Timber.d(Sending request %s on %s%n%s, request.url, chain.connection(), request.headers) val response chain.proceed(request) val t2 System.nanoTime() Timber.d(Received response for %s in %.1fms%n%s, response.request.url, (t2 - t1) / 1e6, response.headers) return response } }7.2 验证JSON数据结构在解析前可以先验证JSON结构是否符合预期public boolean validateResponse(OneNetResponse response) { if (response null) { return false; } if (response.getCode() ! 0) { return false; } if (response.getData() null || response.getData().isEmpty()) { return false; } for (DeviceData data : response.getData()) { if (data.getIdentifier() null || data.getValue() null) { return false; } } return true; }8. 单元测试策略8.1 测试JSON解析逻辑public class OneNetResponseTest { private Gson gson new Gson(); Test public void testParseResponse() { String json {\code\:0,\data\:[{\identifier\:\temp\,\value\:\25.5\}],\msg\:\success\}; OneNetResponse response gson.fromJson(json, OneNetResponse.class); assertEquals(0, response.getCode()); assertEquals(success, response.getMsg()); assertEquals(1, response.getData().size()); assertEquals(temp, response.getData().get(0).getIdentifier()); assertEquals(25.5, response.getData().get(0).getValue()); } }8.2 测试网络请求封装public class OneNetApiClientTest { Test public void testBuildRequest() { OneNetApiClient client new OneNetApiClient(); String token test_token; String productId test_product; String deviceName test_device; // 使用MockWebServer进行测试 MockWebServer server new MockWebServer(); server.start(); server.enqueue(new MockResponse() .setBody({\code\:0,\data\:[],\msg\:\success\}) .addHeader(Content-Type, application/json)); client.setBaseUrl(server.url(/).toString()); // 执行测试 // 验证请求构建和响应处理逻辑 } }

相关文章:

告别文档踩坑:手把手教你用OkHttp和Gson解析OneNET API返回的复杂JSON数据

告别文档踩坑:手把手教你用OkHttp和Gson解析OneNET API返回的复杂JSON数据 在Android开发中,处理网络请求和JSON数据解析是每个开发者都必须掌握的基本技能。然而,当面对像OneNET这样的物联网平台返回的复杂嵌套JSON结构时,即使是…...

ChromaControl终极指南:如何用一个软件控制所有RGB设备?[特殊字符]

ChromaControl终极指南:如何用一个软件控制所有RGB设备?🎮 【免费下载链接】ChromaControl 3rd party device lighting support for Razer Synapse. 项目地址: https://gitcode.com/gh_mirrors/ch/ChromaControl 你是否厌倦了桌面上堆…...

不只是F5隐写:一次CTF解题,带你深入理解ZIP伪加密的底层原理与手动修复

深入解析ZIP伪加密:从CTF实战到二进制手动修复 在CTF竞赛中,ZIP伪加密一直是Misc类题目的经典考点。不同于常规的加密破解,伪加密巧妙地利用了ZIP文件格式的设计特性,在不实际加密数据的情况下制造出需要密码的假象。本文将带您深…...

ScienceDecrypting终极指南:如何永久解锁您的加密学术文献

ScienceDecrypting终极指南:如何永久解锁您的加密学术文献 【免费下载链接】ScienceDecrypting 破解CAJViewer带有效期的文档,支持破解科学文库、标准全文数据库下载的文档。无损破解,保留文字和目录,解除有效期限制。 项目地址…...

基于SUMO与PPO的智能换道决策实战:从环境构建到模型部署

1. 环境准备与基础配置 在开始构建智能换道决策系统之前,我们需要先搭建好开发环境。这里我推荐使用Anaconda来管理Python环境,它能很好地解决不同项目之间的依赖冲突问题。我习惯为每个项目创建独立的环境,比如这次我们可以命名为"sumo…...

高级磁盘空间管理:WinDirStat深度配置与自动化清理指南

高级磁盘空间管理:WinDirStat深度配置与自动化清理指南 【免费下载链接】windirstat WinDirStat is a disk usage statistics viewer and cleanup tool for Microsoft Windows 项目地址: https://gitcode.com/gh_mirrors/wi/windirstat 在当今数据爆炸的时代…...

终极Windows更新修复指南:5分钟解决系统更新问题

终极Windows更新修复指南:5分钟解决系统更新问题 【免费下载链接】Reset-Windows-Update-Tool Troubleshooting Tool with Windows Updates (Developed in Dev-C). 项目地址: https://gitcode.com/gh_mirrors/re/Reset-Windows-Update-Tool 你是否遇到过Wind…...

AI临床研究助手会先在哪些环节跑出来,真正的效率杠杆是什么

AI 临床研究助手最先落地的地方,不会是直接替代研究者做关键判断,而是进入高频、重复、可审计、边界清晰的研究流程节点。本文从技术架构角度拆解它会优先出现在哪些环节,以及开发团队如何用 workflow engine、LLM API、audit log 和 metrics…...

Windows HEIC缩略图解决方案:告别格式壁垒,实现跨平台无缝浏览

Windows HEIC缩略图解决方案:告别格式壁垒,实现跨平台无缝浏览 【免费下载链接】windows-heic-thumbnails Enable Windows Explorer to display thumbnails for HEIC/HEIF files 项目地址: https://gitcode.com/gh_mirrors/wi/windows-heic-thumbnails…...

Agent+可穿戴设备:心率、睡眠、活动数据如何变成有价值的健康建议

可穿戴设备每天都会产生心率、睡眠、步数、活动强度等数据,但开发者真正要解决的不是“采集更多指标”,而是把这些指标转成可解释、可追踪、可配置的健康提示。本文从工程角度搭建一个简化版 Agent 服务,演示如何完成数据接入、趋势计算、规则…...

【量化】IPTQ-ViT: Post-Training Quantization of Non-linear Functions for Integer-only Vision Transformer

【PTQ】PTQViT/IPTQ-ViT (arXiv 2022) 问题: ViT 中的非线性函数(GELU、Softmax)在纯整数推理中存在计算障碍。 核心创新: 模块方法作用多项式近似 GELU用低阶多项式逼近 GELU将非线性运算转化为整数可执行的乘加Bit-shifting Softmax用位移操作近似 …...

信步SV-33A66嵌入式主板:工业智能终端的核心硬件选型与实战解析

1. 项目概述:为什么嵌入式主板是智能终端的“心脏”?在智能设备无处不在的今天,从街角的自助售货机、医院的医疗检测仪,到工厂的自动化产线,这些看似形态各异的设备背后,都有一个共同的“大脑”在默默工作—…...

顶伯在线语音工具背后的技术力量:AI语音合成与深度学习解析

顶伯在线语音工具背后的技术力量在人工智能浪潮中,语音交互正成为人机沟通的核心方式。顶伯作为行业领先的在线语音工具,凭借自主研发的深度学习架构,将文字转化为高度自然的语音,广泛应用于有声阅读、智能客服、教育辅助等领域。…...

【新手专属】OpenClaw 一键安装包:Windows 完整部署流程(含安装包)

OpenClaw 一键安装包|一键部署,告别复杂环境配置 适配系统:Windows 10/11 64 位当前版本:v2.7.5(虾壳云版)核心优势:全程可视化操作,无需命令行、无需手动配置 Python/Node.js&…...

特征对高效数值算法及在船舶轴系振动计算中的应用【附仿真】

✨ 长期致力于特征值与特征向量、对称三对角矩阵、振动计算、船舶推进轴系、并行计算研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)分治并行三对角特…...

百考通:AI赋能期刊论文写作,智能生成优质内容

在学术研究领域,期刊论文的撰写是成果输出的关键环节,却也让众多科研工作者与学生倍感压力:选题迷茫、逻辑梳理困难、格式规范复杂、内容提炼耗时,严重拖慢了学术成果的发表节奏。百考通(https://www.baikaotongai.com…...

从ColorDialog到FontDialog:手把手教你定制WinForm功能对话框,打造个性化桌面应用

从ColorDialog到FontDialog:WinForm功能对话框的深度定制与用户体验优化 在桌面应用开发中,对话框不仅是功能实现的工具,更是用户体验的重要组成部分。想象一下,当用户在使用你的文本编辑器时,能够像专业软件那样流畅地…...

别再乱用sudo了!麒麟KYLINOS下用ACL实现安全的精细化权限控制

麒麟KYLINOS权限管理革命:用ACL替代sudo的精细化控制实战 在麒麟KYLINOS操作系统中,许多管理员习惯性地使用sudo或简单粗暴的chmod 777来解决权限问题,这种"一刀切"的做法实际上为系统安全埋下了重大隐患。想象一下这样的场景&…...

【实战】Latex|在保留ACM-Reference-Format格式的前提下,实现参考文献按引用顺序排列

1. 问题背景与核心痛点 当你使用ACM官方模板撰写论文时,参考文献格式要求必须采用ACM-Reference-Format样式。这个格式有个让人头疼的特性:它会强制按作者姓氏字母顺序排列参考文献,而不是按照文中实际引用顺序。想象一下,你精心设…...

别让严谨变成AI味!实测5大主流降AI工具,这款能完美保留原格式

最近看了一些行业报告,AI工具在写作方面的普及率真的已经超乎想象了。 很多大学生在写论文时也都习惯用AI来辅助寻找灵感、提高效率。 与此同时,相关部门针对人工智能写作出台了一系列规定,各大学术检测平台也都在不断升级AIGC检测算法。 现…...

FPGA存储资源怎么选?一张图看懂LUTRAM、BRAM和URAM的适用场景与性能差异

FPGA存储资源选型指南:LUTRAM、BRAM与URAM的深度对比与实战选择 在FPGA设计的世界里,存储资源的选择往往决定了整个系统的性能和效率。想象一下,你正在为一个高性能图像处理系统设计FPGA架构,需要在片上实现一个容量为128Kb的帧缓…...

零基础也能学!收藏这份AI大模型入门指南,开启你的高薪之路

本文介绍了AI大模型在当前科技趋势中的核心地位,以及各行各业对AI人才的迫切需求。文章指出,即使没有技术基础,普通人也能通过学习应用开发路线掌握AI技能,并提供了循序渐进的学习步骤,包括打好Python编程基础、学习提…...

告别Nginx配置!用miniserve在Windows/Mac/Linux三分钟内搞定文件共享

告别Nginx配置!用miniserve在Windows/Mac/Linux三分钟内搞定文件共享 你是否曾在团队协作时,为了快速分享一个安装包或设计稿,不得不忍受FTP的繁琐配置?或是被Nginx的虚拟主机设置搞得头晕目眩?现在,这一切…...

基于HalloWing的动态眼睛驯鹿面具制作:嵌入式系统与互动艺术的融合实践

1. 项目概述:当驯鹿面具“活”过来几年前我第一次在Maker Faire上看到那些会眨眼、会转动的电子眼睛道具时,就被深深吸引了。那种将静态面具赋予生命力的魔法,一直让我心痒痒。直到我遇到了Adafruit的HalloWing开发板,这个专为“眼…...

大模型小白必看:收藏!揭秘京东面试官如何破解多轮RAG“越聊越蠢”的难题

本文深入剖析多轮RAG在对话场景中容易出现的问题——越聊越“蠢”,即系统无法准确理解用户意图。文章指出,主要原因是历史对话内容污染了当前检索query,导致检索偏离用户真实意图。作者提出了四点判断框架:区分四类对象、检索quer…...

Windows防撤回补丁终极指南:微信QQ消息永久保存的完整解决方案

Windows防撤回补丁终极指南:微信QQ消息永久保存的完整解决方案 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁(我已经看到了,撤回也没用了) 项目地址: https://gi…...

版本控制系统核心功能解析:从历史追踪到团队协作的四大基石

1. 项目概述:从ICO到VCS,一次版本控制的深度对话在软件开发的日常里,我们经常听到“版本控制”这个词,它就像是程序员们的时光机和后悔药。但具体到工具上,Git、SVN、Mercurial……选择很多,而“VCS ICO”这…...

Java Stream流式编程实战

前言 在现代软件开发中,Java Stream流式编程实战是一个非常重要的技术点。本文将从原理到实践,带你深入理解这一技术,并通过完整的代码示例帮助你快速掌握核心知识点。 核心概念 基本原理 Java Stream流式编程实战的核心在于理解其底层机制。…...

解放你的B站缓存视频:3步让m4s文件变身为通用MP4格式

解放你的B站缓存视频:3步让m4s文件变身为通用MP4格式 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经在B站缓存了精彩的教…...

从设计到验证:如何用ADS的HB2TonePAE_FPswp模板快速评估你的PA线性度?

射频功放线性度评估实战:ADS高级仿真模板深度解析 在射频功率放大器(PA)的设计流程中,线性度评估往往是最耗时的环节之一。传统方法需要工程师手动搭建测试平台,不仅效率低下,还容易引入人为误差。Keysight ADS软件内置的HB2ToneP…...