欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目https://atomgit.com/OpenHarmonyPCDeveloper
AtomGit 仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_go_cgo
本文讲解鸿蒙PC基于musl库、应用沙箱与二进制强制签名机制,不原生支持Go语言,通用Linux编译产物无法直接运行。需借助社区Harmonybrew包管理器搭建开发环境:纯Go开发安装go与ohos-sdk,依托SDK实现编译自动签名;CGO跨语言开发需额外安装llvm-gcc-compat补齐cc 编译命令,编译时手动开启CGO参数。搭配CodeArts IDE可完成全流程开发,同时需提前处理软件冲突、使用原生终端规避环境报错。
可以参考OpenHarmony 鸿蒙 PC + CodeArts IDE 实现 Go开发完整开发环境搭建指南
一、copier 是什么
github.com/jinzhu/copier是 Go 结构体自动复制映射库。
日常开发经常需要:实体结构体 ↔ DTO/VO/分页输出结构体,手动逐个字段赋值代码冗余、容易漏字段、改结构体就要同步改复制逻辑;
copier 自动根据字段名匹配复制值,支持同名字段、忽略字段、指定映射、切片批量复制、嵌套结构体复制。
核心作用
- 结构体之间自动拷贝同名字段(string/int/bool/time/嵌套struct全部支持)
- 支持数组/切片批量复制([]User → []UserVO)
- 支持忽略指定字段、自定义字段映射、覆盖值
- 支持深拷贝嵌套结构体,不用手动递归赋值
- 适用于:数据库Model → 返回前端VO、入参DTO → 数据库Model转换
安装
goenv-wGOPROXY=https://goproxy.cn,direct go mod init copier-demo go get github.com/jinzhu/copier完整无报错 main.go
packagemainimport("fmt""github.com/jinzhu/copier""time")// UserModel 数据库源结构体typeUserModelstruct{IDuintUsernamestringPhonestringAgeintBalancefloat64CreateAt time.Time Passwordstring// 敏感密码,不需要复制到VO}// UserVO 返回前端目标结构体// copier:"-" 代表此字段不参与复制typeUserVOstruct{IDuintUsernamestringPhonestringAgeintBalancefloat64CreateAtstringPasswordstring`copier:"-"`// 忽略此字段复制}// 嵌套结构体示例typeOrderModelstruct{OrderIDstringUIDuintPricefloat64User UserModel}typeOrderVOstruct{OrderIDstringUIDuintPricefloat64User UserVO}funcmain(){// 1. 单个结构体 Model -> VO 基础复制fmt.Println("===== 单个结构体复制 =====")model:=UserModel{ID:1001,Username:"lisi",Phone:"13800138000",Age:24,Balance:199.99,CreateAt:time.Now(),Password:"abc123456",}varvo UserVO// 仅使用合法选项:DeepCopy深拷贝,无Ignoreerr:=copier.CopyWithOption(&vo,&model,copier.Option{DeepCopy:true,})iferr!=nil{panic(err)}fmt.Printf("转换后VO:%+v\n",vo)// 2. 切片批量复制 []Model → []VOfmt.Println("\n===== 切片批量复制 =====")modelList:=[]UserModel{{ID:1,Username:"zhangsan",Phone:"13800000001",Age:20,CreateAt:time.Now()},{ID:2,Username:"wangwu",Phone:"13800000002",Age:26,CreateAt:time.Now()},}varvoList[]UserVO err=copier.Copy(&voList,&modelList)iferr!=nil{panic(err)}for_,item:=rangevoList{fmt.Printf("列表VO:%+v\n",item)}// 3. 嵌套结构体复制fmt.Println("\n===== 嵌套结构体复制 =====")orderModel:=OrderModel{OrderID:"ORD001",UID:1001,Price:299.0,User:model,}varorderVO OrderVO err=copier.CopyWithOption(&orderVO,&orderModel,copier.Option{DeepCopy:true,})iferr!=nil{panic(err)}fmt.Printf("嵌套订单VO:%+v\n",orderVO)// 4. IgnoreEmpty 示例:只复制非空字段,不覆盖目标原有值fmt.Println("\n===== 忽略源空值复制(IgnoreEmpty) =====")oldVO:=UserVO{ID:999,Username:"oldName",CreateAt:"2025-01-01"}// IgnoreEmpty=true 源零值不会覆盖目标已有数据err=copier.CopyWithOption(&oldVO,&model,copier.Option{DeepCopy:true,IgnoreEmpty:true,})fmt.Printf("IgnoreEmpty复制后:%+v\n",oldVO)}运行
go run main.gocopier是 Go 后端最常用的结构体映射复制工具,核心场景:数据库 Model ↔ 前端 VO/DTO 转换、切片批量复制、嵌套结构体递归拷贝,自动匹配同名字段,支持忽略字段、深拷贝、空值忽略等能力,省去手动逐个字段赋值样板代码。
一、库简介
github.com/jinzhu/copier是Go开发最常用的结构体转换工具,专门解决数据库Model ↔ 前端VO/DTO对象拷贝,替代手动逐个字段赋值,支持:普通结构体、切片批量复制、嵌套结构体、忽略字段、忽略空值、深拷贝等能力。
安装依赖:
go get github.com/jinzhu/copier二、头部导入
packagemainimport("fmt""github.com/jinzhu/copier""time")fmt:打印输出copier:对象拷贝核心库time:时间类型,演示结构体时间字段自动转字符串
三、结构体定义解析
1. 单层用户 Model & VO
// UserModel 数据库源结构体typeUserModelstruct{IDuintUsernamestringPhonestringAgeintBalancefloat64CreateAt time.Time Passwordstring// 敏感密码,不需要复制到VO}// UserVO 返回前端目标结构体// copier:"-" 代表此字段不参与复制typeUserVOstruct{IDuintUsernamestringPhonestringAgeintBalancefloat64CreateAtstringPasswordstring`copier:"-"`// 忽略此字段复制}- Model:数据库映射实体,包含完整字段、密码、
time.Time时间类型 - VO:给前端返回的视图对象,剔除敏感字段、类型适配(
time.Time→ string) - 标签
copier:"-":标记该字段跳过拷贝,源对象Password不会赋值到VO,防止密码泄露
2. 嵌套结构体 OrderModel / OrderVO
typeOrderModelstruct{OrderIDstringUIDuintPricefloat64User UserModel// 嵌套Model}typeOrderVOstruct{OrderIDstringUIDuintPricefloat64User UserVO// 嵌套VO}copier 自动识别嵌套结构体,递归完成内部子结构体转换,无需手动单独拷贝User。
四、main 四大场景代码拆解
场景1:单个结构体 Model → VO 基础深拷贝
model:=UserModel{ID:1001,Username:"lisi",Phone:"13800138000",Age:24,Balance:199.99,CreateAt:time.Now(),Password:"abc123456",}varvo UserVO// 仅使用合法选项:DeepCopy深拷贝,无Ignoreerr:=copier.CopyWithOption(&vo,&model,copier.Option{DeepCopy:true,})iferr!=nil{panic(err)}fmt.Printf("转换后VO:%+v\n",vo)核心API说明
copier.Copy(dst, src):基础拷贝函数copier.CopyWithOption(dst, src, option):带自定义配置拷贝DeepCopy: true:开启深拷贝,指针、嵌套结构体、切片不会共用底层内存,修改VO不会影响原Model- 自动类型转换:源
time.Time自动转为VO的string字符串时间 - Password 带
copier:"-",VO.Password 保持空字符串,不会复制密码
场景2:切片批量复制 []Model → []VO
modelList:=[]UserModel{{ID:1,Username:"zhangsan",Phone:"13800000001",Age:20,CreateAt:time.Now()},{ID:2,Username:"wangwu",Phone:"13800000002",Age:26,CreateAt:time.Now()},}varvoList[]UserVO err=copier.Copy(&voList,&modelList)iferr!=nil{panic(err)}for_,item:=rangevoList{fmt.Printf("列表VO:%+v\n",item)}支持切片直接批量转换,不用循环遍历逐个拷贝,查询数据库列表后一键转前端返回数组,大幅简化代码。
场景3:嵌套结构体自动递归拷贝
orderModel:=OrderModel{OrderID:"ORD001",UID:1001,Price:299.0,User:model,}varorderVO OrderVO err=copier.CopyWithOption(&orderVO,&orderModel,copier.Option{DeepCopy:true,})无需手动处理内部User,copier 自动识别内部结构体,将UserModel完整转换为UserVO,多层嵌套同样支持递归拷贝。
场景4:IgnoreEmpty 忽略源空值,不覆盖目标原有数据
oldVO:=UserVO{ID:999,Username:"oldName",CreateAt:"2025-01-01"}// IgnoreEmpty=true 源零值不会覆盖目标已有数据err=copier.CopyWithOption(&oldVO,&model,copier.Option{DeepCopy:true,IgnoreEmpty:true,})fmt.Printf("IgnoreEmpty复制后:%+v\n",oldVO)IgnoreEmpty: true:如果源结构体字段是零值(空字符串、0、nil),不会覆盖目标对象已存在的值- 适用场景:表单局部更新,只传部分字段,保留原有未修改字段
五、核心标签与配置参数汇总
1. 结构体标签
copier:"-":忽略此字段,不参与拷贝copier:"TargetFieldName":字段名不一致时手动映射,例Name stringcopier:“UserName”`
2. Option 常用配置
| 参数 | 作用 |
|---|---|
| DeepCopy | 开启深拷贝,嵌套/指针推荐开启 |
| IgnoreEmpty | 源零值不覆盖目标字段 |
| IgnoreNil | 源nil指针不覆盖目标 |
六、运行输出关键特征
- VO 的 Password 为空,密码不会复制;
- CreateAt 从 time.Time 自动转为字符串时间;
- 嵌套订单VO内User已经完成Model转VO;
- IgnoreEmpty场景下,oldVO原有旧字段不会被源零值覆盖。
七、项目使用优势与注意事项
优势
- 省去大量手动字段赋值代码,减少重复工作量;
- 自动兼容基础类型转换、时间类型、切片、嵌套结构体;
- 支持敏感字段忽略、局部更新忽略空值,适配CRUD业务;
- API简洁,单函数完成单条/批量转换。
生产注意点
- 涉及嵌套、指针结构体务必开启
DeepCopy: true,避免浅拷贝数据互相污染; - 密码、身份证等敏感字段必须添加
copier:"-"禁止拷贝到返回VO; - 字段名不一致时使用映射标签手动绑定;
- 拷贝后建议捕获err,示例中panic仅用于演示,线上改为日志打印错误。
一、安装依赖
go get github.com/jinzhu/copier导入依赖:
import("fmt""github.com/jinzhu/copier""time")二、结构体定义分层说明(后端标准分层思想)
1. UserModel 数据库实体(Model)
从数据库查询出来的原始结构体,包含完整字段、敏感字段、原生time.Time时间类型
typeUserModelstruct{IDuintUsernamestringPhonestringAgeintBalancefloat64CreateAt time.Time// 原生时间类型Passwordstring// 敏感密码,不能返回前端}2. UserVO 前端返回视图对象(VO)
对外接口返回给前端的结构体:
- 移除敏感字段逻辑:同名字段上标记
copier:"-"直接跳过复制 - 类型转换需求:
time.Time→string字符串时间,copier 自动兼容基础类型转换
typeUserVOstruct{IDuintUsernamestringPhonestringAgeintBalancefloat64CreateAtstring// 字符串,适配前端Passwordstring`copier:"-"`// tag标记:复制时忽略此字段}- Tag
copier:"-"核心作用:即使源结构体存在同名字段,也不复制该值,专门用于密码、密钥、内部ID等敏感信息脱敏。
3. 嵌套结构体 OrderModel / OrderVO
模拟业务一对一场景:订单内嵌套用户信息
typeOrderModelstruct{OrderIDstringUIDuintPricefloat64User UserModel// 嵌套数据库实体}typeOrderVOstruct{OrderIDstringUIDuintPricefloat64User UserVO// 嵌套前端VO}copier 开启DeepCopy: true后会递归自动复制内部嵌套结构体,不用手动单独拷贝内层 User。
三、核心API区分
copier.Copy(dst, src)
基础浅拷贝,自动匹配同名字段,适合简单结构体、切片批量复制,无自定义配置。copier.CopyWithOption(dst, src, copier.Option{})
带自定义配置拷贝,支持深拷贝、忽略空值、字段映射等高级功能。- 入参规则:第一个参数目标结构体指针,第二个源结构体指针。
四、分模块逐代码解析
模块1:单个结构体 Model → VO 基础深拷贝
model:=UserModel{ID:1001,Username:"lisi",Phone:"13800138000",Age:24,Balance:199.99,CreateAt:time.Now(),Password:"abc123456",}varvo UserVO err:=copier.CopyWithOption(&vo,&model,copier.Option{DeepCopy:true,})iferr!=nil{panic(err)}fmt.Printf("转换后VO:%+v\n",vo)关键配置DeepCopy: true深拷贝
- 浅拷贝默认:只复制基础数据类型(int/string/float),引用类型(切片、map、嵌套结构体)只复制内存地址,内外结构体修改会互相影响。
- 深拷贝:完整递归复制所有嵌套数据,生成全新独立内存,修改 VO 不会影响原 Model,业务开发推荐始终开启。
自动执行逻辑
- 匹配所有同名字段:ID、Username、Phone、Age、Balance 直接复制;
CreateAt源time.Time自动转为 VO 的string;Password标记copier:"-",完全跳过,VO.Password 保持空字符串;- 敏感密码不会暴露给前端,完美实现脱敏。
模块2:切片批量复制 []Model → []VO
modelList:=[]UserModel{{ID:1,Username:"zhangsan",Phone:"13800000001",Age:20,CreateAt:time.Now()},{ID:2,Username:"wangwu",Phone:"13800000002",Age:26,CreateAt:time.Now()},}varvoList[]UserVO err=copier.Copy(&voList,&modelList)iferr!=nil{panic(err)}for_,item:=rangevoList{fmt.Printf("列表VO:%+v\n",item)}核心优势
不用手写 for 循环逐个 new VO、赋值字段,一行代码完成整个切片批量转换。
适用场景:数据库分页查询列表,批量转接口返回VO数组。
模块3:嵌套结构体递归拷贝
orderModel:=OrderModel{OrderID:"ORD001",UID:1001,Price:299.0,User:model,}varorderVO OrderVO err=copier.CopyWithOption(&orderVO,&orderModel,copier.Option{DeepCopy:true,})fmt.Printf("嵌套订单VO:%+v\n",orderVO)开启DeepCopy后自动递归处理内部User嵌套结构:
- 外层 OrderID、UID、Price 直接复制;
- 内层
User(UserModel)自动完整拷贝到User(UserVO); - 内层 Password 同样遵循
copier:"-"忽略规则。
不开启深拷贝时嵌套结构体只会拷贝指针,数据不隔离,极易产生副作用。
模块4:IgnoreEmpty 忽略源空值,不覆盖目标原有数据
oldVO:=UserVO{ID:999,Username:"oldName",CreateAt:"2025-01-01"}err=copier.CopyWithOption(&oldVO,&model,copier.Option{DeepCopy:true,IgnoreEmpty:true,})fmt.Printf("IgnoreEmpty复制后:%+v\n",oldVO)IgnoreEmpty: true作用规则
源结构体中零值/空值字段不会覆盖目标结构体已存在的值:
- 源 model.ID=1001(非空)→ 覆盖 oldVO.ID=999;
- 如果源某个字段为空(如源 Phone=“”),则不会修改 oldVO 原有 Phone;
业务场景
局部更新接口:只传部分字段更新,保留前端原有未传字段数据,避免被空值清空。
五、补充常用 Tag 拓展(示例只用到copier:"-",完整常用标签)
copier:"-":忽略此字段,不复制copier:"sourceFieldName":字段名不一致手动映射
typeUserVOstruct{UserIDuint`copier:"ID"`// 源字段ID → 目标UserID}copier:"rename"批量统一命名转换,配合数据库下划线、前端驼峰使用
六、Option 完整配置字段说明
copier.Option{DeepCopy:true,// 开启深拷贝,推荐必开IgnoreEmpty:true,// 源空值不覆盖目标IgnoreZero:true,// 数字0、空字符串都视为空值忽略FieldMapper:map[string]string{},// 全局字段映射}七、业务开发核心价值总结
- 消除样板代码:不用手动写几十行
vo.Name = model.Name; - 分层解耦:Model 存库、VO 返回前端,敏感字段一键脱敏;
- 批量处理:切片数组一键转换,分页列表开发效率极高;
- 嵌套支持:自动递归复制嵌套结构体,适配订单、详情等复杂业务;
- 灵活控制:支持忽略空值、自定义字段映射,适配更新接口、特殊字段转换;
- 类型自动适配:time.Time ↔ string、int ↔ uint 等基础类型自动转换。
八、常见注意事项
- 传参必须传指针,否则拷贝不会生效;
- 嵌套结构体、切片、map 场景务必开启
DeepCopy,防止引用共享; - 密码、token、内部主键一律用
copier:"-"屏蔽,防止接口泄露; IgnoreEmpty适合局部更新,查询列表场景一般关闭,需要完整覆盖目标结构体。
核心API说明
copier.Copy(dst, src)
基础复制,自动匹配同名字段,支持结构体、切片。copier.CopyWithOption(dst, src, copier.Option{})
带配置复制,常用配置:
Ignore []string:忽略字段,支持嵌套User.PasswordDeepCopy: true:开启深拷贝,修改源不影响目标
- 规则
- 字段名完全匹配才复制(大小写一致)
- time.Time 会自动转字符串填入string字段
- 数字类型自动兼容转换(uint/int/float64)
- 嵌套结构体自动递归复制
业务场景
- ORM查询出来的数据库Model,快速转为给前端返回的VO,不用手动赋值几十行代码
- 批量列表转换,不用循环逐个赋值
- DTO入参复制到数据库保存Model
- 多层嵌套实体转换,简化嵌套赋值逻辑