yml文件上傳并映射到實體類

文章目錄

      • 功能背景
      • 功能需要
      • 前端開發
        • 組件選用
        • 組件嵌套和參數綁定
        • 上傳邏輯示例
      • 后端開發
        • 接收邏輯
        • 解析邏輯
          • 省流
          • 純手動實現(不建議)

功能背景

開發一個配置文件解析功能,需要兼容老版本的配置文件。

功能需要

  1. 前端:兩個配置文件分別上傳
  2. 后端:配置文件解析、分版本匹配、配置文件映射到實體類

前端開發

組件選用

選用element-plus的el-pload組件進行上傳控制,核心組件代碼為:

<el-uploadv-model:file-list="fileListModel":on-remove="handleRemove":before-remove="beforeRemove":limit="1":on-exceed="handleExceed":auto-upload="false"class="upload-location"accept=".yml,.yaml"
/>

核心參數說明:

屬性作用
v-model:file-list綁定上傳文件列表
:on-remove文件移除時觸發的回調函數,移除文件是組件自發的,此處綁的是你想在觸發該邏輯時做的操作
:before-remove文件移除前觸發的函數,此處我綁定了一個確認彈框
:limit="1"限制最多上傳 1 個文件
:on-exceed超出文件數量限制時的回調,我綁了個提示框
:auto-upload="false"不自動上傳,手動觸發上傳,因為我要一次提交兩個不同的配置文件
class="upload-location"設置樣式類
accept=".yml,.yaml"限制可上傳的文件類型,此處我設置的是yaml類型

組件嵌套和參數綁定

此處我選擇以el-upload組件為核心,將涉及的提示操作等封裝成一個自定義組件。在父組件中使用兩次該子組件,
并通過defineModel 實現父組件和子組件間值的雙向綁定。
el-upload需要綁定的類型為UploadFile數組,即

子組件:const fileListModel = defineModel<UploadFile[]>("fileList"); 
父組件:const fileList = ref<any[]>([]);  
綁定:v-model:file-list="fileList"

UploadFile參數為:

export interface UploadFile {uid: number | stringname: stringstatus?: 'ready' | 'uploading' | 'success' | 'fail'size?: numberpercentage?: numberraw?: Fileresponse?: anyurl?: stringtype?: string
}

其中raw為我們需要向后端傳遞的數據部分。

上傳邏輯示例
const formData = new FormData()
//實際需要進行判空,此處只寫核心部分
formData.append('file1', fileList1.value[0].raw) 
formData.append('file2', fileList2.value[0].raw) 
axios.post('/api/upload', formData, {headers: {'Content-Type': 'multipart/form-data'}
}).then(res => {console.log('上傳成功:', res.data)
}).catch(err => {console.error('上傳失敗:', err)
})

后端開發

接收邏輯
@PostMapping("/upload")
public R uploadTask(@RequestParam(value = "file1", required = false) MultipartFile file1,@RequestParam(value = "file2", required = true) MultipartFile file2,@RequestParam(value = "groupId", required = true) Integer groupId) {if (file2 == null || groupId == null) {return R.fail("IMPORT_NOT_EXIST_PARAMS");}if (file1 != null) {return paramService.importTaskParams(file1, file2, groupId);} else {return paramService.importTaskParams(file2, groupId);}
}
解析邏輯

單純的解析yaml文件并映射比較簡單,無非將傳入的文件內容使用YAMLMapper解析一下,但本任務有一個要求:

  1. 兼容之前的yml配置文件寫法

注:
之前的配置文件使用了 Spring Boot 提供的“松散綁定(Relaxed Binding)”機制,這是直接上傳yaml文件并解析無法直接辦到的,由此延伸出幾點需求:

  1. 支持單字符串向數組類型的映射(即支持a,b,c,d寫法);
  2. 支持單字符串向枚舉類型忽略大小寫的映射;
  3. 中劃線和駝峰類型雙兼容(即既能解析中劃線寫法的配置文件,又能解析駝峰寫法的配置文件)
  4. 16進制單字符向char類型的映射
省流

手動調用 Spring Boot 提供的綁定工具,不必自己實現。

高效實現:

try ( InputStream inputStream = multipartFile.getInputStream()){// 1. 讀取 YAML 文件Yaml yaml = new Yaml();//假設沒有上層需要排除Map<String, Object> yamlMap = yaml.load(inputStream);// 2. 平鋪嵌套結構(嵌套結構必須用)Map<String, Object> flatMap = flattenMap(yamlMap, null);// 3. 構造 PropertySource(Spring Boot Binder 需要它)PropertySource<?> propertySource = new MapPropertySource("customYaml", flatMap);StandardEnvironment env = new StandardEnvironment();env.getPropertySources().addFirst(propertySource);// 4. 使用 Binder 綁定Binder binder = Binder.get(env);return binder.bind("", Bindable.of(YourConfig.class)).orElseThrow(() -> new RuntimeException("Binding failed"));} catch (Exception e) {e.printStackTrace();}

輔助方法:

    // 將嵌套結構扁平化成 "a.b.c" 格式,只有這樣才能處理子級別的中劃線映射駝峰private Map<String, Object> flattenMap(Map<String, Object> source, String parentKey) {Map<String, Object> result = new HashMap<>();for (Map.Entry<String, Object> entry : source.entrySet()) {String key = (parentKey != null ? parentKey + "." : "") + entry.getKey();Object value = entry.getValue();if (value instanceof Map) {result.putAll(flattenMap((Map<String, Object>) value, key));} else {result.put(key, value);}}return result;}
純手動實現(不建議)
  • 單字符串向數組類型的映射
    • 重寫set方法,使得數組類型屬性可接受非數組類型參數。
  • 單字符串向枚舉類型忽略大小寫的映射
    • 手寫模糊獲取方法,并在set方法中調用 。
  • 中劃線和駝峰類型雙兼容
    • 重構映射邏輯,在映射之前加一段處理,即將原本的中劃線名字修改為駝峰,這樣就可和實體類中的屬性實現匹配,具體實現為:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;public class FlexibleObjectMapper extends ObjectMapper {public FlexibleObjectMapper() {// 注冊自定義模塊用于字段名轉換SimpleModule module = new SimpleModule();module.setDeserializerModifier(new BeanDeserializerModifier() {@Overridepublic JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,BeanDescription beanDesc,JsonDeserializer<?> deserializer) {if (deserializer instanceof BeanDeserializer) {return new FlexibleCaseDeserializer((BeanDeserializer) deserializer);}return deserializer;}});this.registerModule(module);}private static class FlexibleCaseDeserializer extends BeanDeserializer {public FlexibleCaseDeserializer(BeanDeserializer base) {super(base);}@Overridepublic Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {//在原邏輯之前加一段處理,即將原本的中劃線名字修改為駝峰,這樣就可和實體類中的屬性實現匹配JsonNode tree = p.getCodec().readTree(p);if (tree.isObject()) {ObjectNode objNode = (ObjectNode) tree;Map<String, JsonNode> newFields = new HashMap<>();objNode.fields().forEachRemaining(entry -> {String fieldName = entry.getKey();String camelCase = toCamelCase(fieldName);newFields.put(camelCase, entry.getValue());});objNode.removeAll();newFields.forEach(objNode::set);JsonParser newParser = objNode.traverse(p.getCodec());newParser.nextToken(); // advance to START_OBJECT//回到原邏輯return super.deserialize(newParser, ctxt);}return super.deserialize(p, ctxt);}private String toCamelCase(String s) {if (!s.contains("_")) return s;StringBuilder sb = new StringBuilder();boolean upper = false;for (char c : s.toCharArray()) {if (c == '_') {upper = true;} else {sb.append(upper ? Character.toUpperCase(c) : c);upper = false;}}return sb.toString();}}
}
  • 16進制單字符向char類型的映射
    • 自定義反序列化邏輯,指定格式字符串向字符的處理。
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;import java.io.IOException;public class CharDeserializer extends JsonDeserializer<Character> {@Overridepublic Character deserialize(JsonParser jsonParser, DeserializationContext context)throws IOException {String text = jsonParser.getText().trim();// 支持 \u 或 u 開頭的十六進制 Unicode 字符if (text.startsWith("\\u") || text.startsWith("u")) {text = text.substring(text.indexOf('u') + 1);try {int code = Integer.parseInt(text, 16);return (char) code;} catch (NumberFormatException e) {throw new IOException("Invalid hexadecimal character: " + text);}}throw new IOException("Empty character string");}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/901586.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/901586.shtml
英文地址,請注明出處:http://en.pswp.cn/news/901586.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ElasticSearch中常用的數據類型

一、映射 Elasticsearch中通過映射來指定字段的數據類型&#xff0c;映射方式有2種&#xff0c;靜態映射和動態映射。 1.動態映射 使用動態映射時&#xff0c;無須指定字段的數據類型&#xff0c;Elasticshearch會自動根據字段內容來判斷映射到哪個數據類型。 比如&#xff…

【神經網絡結構的組成】深入理解 轉置卷積與轉置卷積核

&#x1f308; 個人主頁&#xff1a;十二月的貓-CSDN博客 &#x1f525; 系列專欄&#xff1a; &#x1f3c0;《深度學習理論直覺三十講》_十二月的貓的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻擋不了春天的腳步&#xff0c;十二點的黑夜遮蔽不住黎明的曙光 …

CSS高度坍塌?如何解決?

一、什么是高度坍塌&#xff1f; 高度坍塌&#xff08;Collapsing Margins&#xff09;是指當父元素沒有設置邊框&#xff08;border&#xff09;、內邊距&#xff08;padding&#xff09;、內容&#xff08;content&#xff09;或清除浮動時&#xff0c;其子元素的 margin 會…

Web前端開發——格式化文本與段落(上)

一、學習目標 網頁內容的排版包括文本格式化、段落格式化和整個頁面的格式化&#xff0c;這是設計個網頁的基礎。文本格式化標記分為字體標記、文字修飾標記。字體標記和文字修飾標記包括對于字體樣式的一些特殊修改。段落格式化標記分為段落標記、換行記、水平分隔線標記等。…

關于PHP開源CMS系統ModStart的詳細介紹及使用指南

關于PHP開源CMS系統ModStart的詳細介紹及使用指南&#xff1a; &#x1f50d; ModStart是什么&#xff1f; 基于Laravel框架開發的模塊化CMS系統采用Apache 2.0 開源協議&#xff0c;完全免費可商用特別適合需要快速搭建企業級網站/管理系統的開發者 &#x1f680; 核心優勢…

TCP標志位抓包

說明 TCP協議的Header信息&#xff0c;URG、ACK、PSH、RST、SYN、FIN這6個字段在14字節的位置&#xff0c;對應的是tcp[13]&#xff0c;因為字節數是從[0]開始數的&#xff0c;14字節對應的就是tcp[13]&#xff0c;因此在抓這幾個標志位的數據包時就要明確范圍在tcp[13] 示例1…

RK3588S開發板將SPI1接口改成GPIO

參考官方教程&#xff1a;ROC-RK3588S-PC 一.基本知識&#xff1a; 1.GPIO引腳計算&#xff1a; ROC-RK3588S-PC 有 5 組 GPIO bank&#xff1a;GPIO0~GPIO4&#xff0c;每組又以 A0~A7, B0~B7, C0~C7, D0~D7 作為編號區分&#xff0c;常用以下公式計算引腳&#xff1a;GPIO…

Java 設計模式:適配器模式詳解

Java 設計模式&#xff1a;適配器模式詳解 適配器模式&#xff08;Adapter Pattern&#xff09;是一種結構型設計模式&#xff0c;它通過將一個類的接口轉換為客戶端期望的另一個接口&#xff0c;使原本不兼容的類能夠協同工作。適配器模式就像現實生活中的電源適配器&#xf…

python manimgl數學動畫演示_微積分_線性代數原理_ubuntu安裝問題[已解決]

1.背景 最近調研python opencv, cuda加速矩陣/向量運算, 對于矩陣的線性變換, 秩, 轉秩, 行列式變化等概概念模糊不清. 大概課本依舊是天書, 于是上B站搜索線性代數, 看到 3Blue1Brown 線性變換本質 視頻, 點擊觀看. 驚為天人 --> 豁然開朗 --> 突然頓悟 --> 開心不已…

Git 學習筆記

這篇筆記記錄了我在git學習中常常用到的指令&#xff0c;方便在未來進行查閱。此篇文章也會根據筆者的學習進度持續更新。 網站分享 Git 常用命令大全 Learn Git Branching 基礎 $ git init //在當前位置配置一個git版本庫 $ git add <file> //將文件添加至…

Dynamics365 ExportPdfTemplateExportWordTemplate兩個Action調用的body構造

這兩天在用ExportPdfTemplate做pdf導出功能時&#xff0c;遇到了如下問題InnerException : Microsoft.OData.ODataException: An unexpected StartArray node was found when reading from the JSON reader. A PrimitiveValue node was expected. 我的場景是使用power automate…

經典算法 判斷一個圖是不是樹

判斷一個圖是不是樹 問題描述 給一個以0 0結尾的整數對列表&#xff0c;除0 0外的每兩個整數表示一條連接了這兩個節點的邊。假設節點編號不超過100000大于0。你只要判斷由這些節點和邊構成的圖是不是樹。是輸出YES&#xff0c;不是輸出NO。 輸入樣例1 6 8 5 3 5 2 6 4 5…

【嵌入式八股2】C++:STL容器與算法

1. STL常見容器及其內部實現的數據結構 序號 名稱 描述 存儲結構 常用方法和操作 1vector動態分配的數組順序數組&#xff08;array&#xff09;v.push_back(), v.pop_back(), v.insert(), v.erase(), v.capacity(), v.size(), v.at(idx), v.front(), v.back()2list雙向鏈表離…

vmcore分析鎖問題實例(x86-64)

問題描述&#xff1a;系統出現panic&#xff0c;dmesg有如下打印&#xff1a; [122061.197311] task:irq/181-ice-enp state:D stack:0 pid:3134 ppid:2 flags:0x00004000 [122061.197315] Call Trace: [122061.197317] <TASK> [122061.197318] __schedule0…

在Apple Silicon上部署Spark-TTS:四大核心庫的技術魔法解析!!!

在Apple Silicon上部署Spark-TTS&#xff1a;四大核心庫的技術魔法解析 &#x1f680; &#xff08;M2芯片實測&#xff5c;Python 3.12.9PyTorch 2.6.0全流程解析&#xff09; 一、核心庫功能全景圖 &#x1f50d; 在Spark-TTS的部署過程中&#xff0c;pip install numpy li…

leetcode03 -- 武漢旅游查詢系統

武漢旅游查詢系統 1 界面展示 1.首頁地圖界面 2.查找功能 在查找框內輸入查找的景點名稱 查找到的景點在地圖上進行定位,右側展示景點的詳細信息。 3.添加景點功能 在地圖上點擊某個位置,系統彈出一個輸入框供用戶填寫景點的名稱和描述。 在彈出的輸入框中輸入景點名…

玩機進階教程----MTK芯片設備刷機導致的死磚修復實例解析 連電腦毫無反應 非硬件問題

在高通芯片機型中,我們可以通過短接主板測試點來激活高通芯片特有的9008底層端口來刷寫救磚固件。但通常MTK芯片類的設備聯機電腦即可觸發深刷模式。但有些例外的情況會導致鏈接電腦毫無反應。遇到類似故障的友友可以參閱此博文嘗試解決。 通過博文了解 1??????-----實…

09-設計模式企業場景 面試題-mk

文章目錄 1.工廠(方法)模式1.1.簡單工廠模式(不是設計模式,是編程習慣)1.2.工廠方法模式(企業開發中最常見)1.3.抽象工廠模式2.策略模式2.1.登錄案例(工廠模式+策略模式)3.責任鏈設計模式4.單點登錄怎么是實現的?5.權限認證是如何實現的6.上傳數據的安全性你們怎么控…

BUUCTF-Web(1-20)

目錄 一.SQL注入 (1)[極客大挑戰 2019]EasySQL 萬能密碼 (7)[SUCTF 2019]EasySQL 堆疊注入 解一&#xff1a; 解二&#xff1a; (10)[強網杯 2019]隨便注 堆疊注入 解一&#xff1a; 解二&#xff1a; 解三&#xff1a; (8)[極客大挑戰 2019]LoveSQL 聯…

軟件包安裝管理Gitlab

官方提供了非常詳盡的系統及自動化腳本安裝教程 Gitlab官網下載地址&#xff1a;https://gitlab.cn/install/ 1、安裝配置 今天我們說一下包安裝管理&#xff0c;這樣方便我們自己更精確的制定符合我們自己需要的Gitlab倉庫 配置&#xff1a;ubuntu2004(focal) 4C8G 下載程…