详解数据转换类(对象)(DTO) / 详解@JSONField

package xxx;

import com.alibaba.fastjson.annotation.JSONField;
...

@Data
public class Container {
    @JSONField(name = "containerNeedList")
    private List<RequestContainer> requestContainer;

    public Set<ExCtnRequest> getObject() {
        Set<ExCtnRequest> exCtnRequests = new HashSet<>();
        if (CollectionUtils.isNotEmpty(requestContainer)) {
            for (RequestContainer item : requestContainer) {
                ExCtnRequest exCtnRequest = new ExCtnRequest();
                exCtnRequest.setIsSoc("SOC".equals(item.getSupplierType()) ? OrderConstants.YES : OrderConstants.NO);
                exCtnRequest.setQuantityOfCtn(item.getQuantity());
 				...
                exCtnRequest.setFclLclEmpty("F");
                exCtnRequests.add(exCtnRequest);
            }
        }
        return exCtnRequests;
    }
}

而前面代码提到的RequestContainer类的定义如下:

package xxx;
import xxx;

@Data
public class RequestContainer {
    //  xxx
    private BigDecimal quantity;
    //  xxx
    private String supplierType;
    ...
}

一、@JSONField注解

@JSONField(name = "containerNeedList")是Fastjson库中一个注解,它用于将Java对象的字段与JSON数据中的字段进行映射。

这个注解的主要作用是指定Java类的字段在序列化和反序列化时与JSON数据的键名进行对应。

具体来说,它告诉Fastjson库在序列化或反序列化时,如何将Java类的属性与JSON中的字段进行匹配。

详细解读:

1.注解来源:@JSONField是Fastjson提供的一个注解,用于控制JSON和Java对象之间的映射。

它属于com.alibaba.fastjson.annotation.JSONField包。

2.作用:name属性指定了Java对象字段对应的JSON字段名称。

也就是说,

当Java对象被转换为JSON字符串时,containerNeedList这个Java字段会映射成JSON字符串中的"containerNeedList"字段

同样,当JSON字符串被反序列化回Java对象时,JSON中"containerNeedList"字段会被映射到Java类的containerNeedList字段上。

3.示例:假设有如下的Java类:

public class Container {

@JSONField(name = "containerNeedList")

private List<RequestContainer> requestContainer;

// getter, setter

}

在这个例子中,

requestContainer是Java类中的字段名,

而@JSONField(name = "containerNeedList")注解表明该字段将被映射为JSON中的"containerNeedList"

序列化示例:假设我们有一个Container对象,其requestContainer字段包含了某些数据:

Container container = new Container();

container.setRequestContainer(someList); //someList是一个List<RequestContainer>类型的数据

使用 Fastjson 库进行序列化时:

String json = JSON.toJSONString(container);

生成的 JSON 字符串将会是这样的:

{

"containerNeedList": [ ... ] // 对应的 List<RequestContainer> 内容

}

即便 Java 类中字段的名称是 requestContainer,

但 JSON 中的字段名却是 "containerNeedList",

这是因为@JSONField(name = "containerNeedList")注解指定了映射关系。

4.用途:

字段名称不一致:当Java类中的字段名称与JSON中的字段名称不一致时,使用@JSONField可以轻松解决映射问题。

兼容性:在进行跨系统数据交换时,可能会遇到字段命名规范不同的情况。使用@JSONField可以保证数据在传输过程中不受影响。

反序列化:当JSON数据反序列化成Java对象时,Fastjson会根据@JSONField注解自动进行字段匹配。

二、@JSONField注解是作用在类Container上了还是requestContainer上了?

注解作用范围

注解一般作用于它所修饰的元素。

在Java中,注解是直接应用在类、字段、方法、参数等元素上的。

在你提到的例子中:@JSONField注解作用于requestContainer字段。

如果你把注解写在方法、类或者变量声明上,它只会影响被注解的那个方法、类或字段。注解不会影响它下面的代码行,注解的作用范围仅限于紧接着它的元素。

三、如果不写@JSONField注解,它俩能一一对应地映射上吗?

默认映射规则

  • 如果 JSON 字段名和 Java 类中的属性名一致,则可以不写 @JSONField 注解,Fastjson 会自动映射。
  • 如果 JSON 字段名和 Java 类中的属性名不一致,则需要写 @JSONField 注解来显式指定映射关系。

四、@JSONField注解既可以作用在类(级别)上,又可以作用在属性(级别)上吗?

@JSONField注解可以用于类字段上,依赖于它被应用的具体位置,作用范围也有所不同。

1. @JSONField注解用于类字段(属性):

  • 作用:当@JSONField注解应用于类的字段(即属性)时,它控制该字段与JSON数据中某个字段的映射。
  • 使用场景:如果Java类中的属性名与JSON字段名不一致,可以通过@JSONField注解来显式地指定映射关系。

例如:

public class Container {
    @JSONField(name = "containerNeedList")
    private List<RequestContainer> requestContainer;
}
  • 在这个例子中,requestContainer属性会映射为JSON 中的 "containerNeedList" 字段。无论该类其他部分如何,只有被标注的字段(requestContainer)与containerNeedList进行映射。

2. @JSONField注解用于类:

  • 作用:@JSONField注解也可以应用于类上,通常用于在序列化时改变JSON对象的根元素名称(即类的名称)。
  • 使用场景:如果你希望 JSON 数据的根对象名和类名不一致,可以在类级别上使用 @JSONField 注解来指定。

例如:

@JSONField(name = "container")
public class Container {
    private List<RequestContainer> requestContainer;
}
  • 在这个例子中,整个Container类的对象在序列化时,JSON对象的根字段将被命名为 container,而不是类名Container。这意味着Container类本身的映射会变成JSON中的container

五、代码解读

首先,代码定义了一个名为Container的类,包含了一个列表,名为requestContainer,类型是List<RequestContainer>,以及一个getObject方法,返回一个Set<ExCtnRequest>类型的集合。

getObject方法的解读:

1.初始化exCtnRequests:

创建了一个HashSet<ExCtnRequest>类型的exCtnRequests集合。这将用于存储转换后的ExCtnRequest对象。

关于Set<ExCtnRequest> exCtnRequests = new HashSet<>();

(1)为啥不能new Set<>()而是new HashSet()<>?

因为:

Set<ExCtnRequest> exCtnRequests = new Set<>(); // ❌ 语法错误
  • Set 是一个接口(interface),接口不能直接被实例化
  • 你必须 new它的一个具体实现类,比如HashSet、LinkedHashSet、TreeSet等

✅ 正确用法:

Set<ExCtnRequest> exCtnRequests = new HashSet<>();

(2)为啥是用Set<ExCtnRequest>类型,而不是 List、Map、或者别的?

因为在业务需求中,你只想要一个"无重复元素"的集合,而不是顺序列表或键值对。

  • Set 的特点:不允许元素重复。
  • List:允许重复元素,有顺序。
  • Map:是键值对结构。

在这段代码里:

exCtnRequests.add(exCtnRequest);

可能会有多个重复的ExCtnRequest对象。如果你不希望这些重复的数据被加入,就用Set。

也就是说:使用Set的目的是为了自动去重!

(3)为啥是HashSet?

  • HashSet是最常用的Set实现,底层是哈希表,插入和查找的性能非常高(平均O(1))。
  • 它不保证顺序(也就是你加入元素的顺序,取出来时可能变了)。
  • 如果你想要一个有顺序的Set,可以考虑LinkedHashSet。
  • 如果你想要一个自动排序的Set,可以考虑TreeSet。

所以:

Set<ExCtnRequest> exCtnRequests = new HashSet<>();

这是一种非常常见、性能优良、去重需求明确的写法

(4)是不是实例化Set的固定搭配就是new HashSet<>()?

是的,如果你没有特殊的顺序需求,new HashSet<>()是最常见、最推荐的默认搭配

总结一波:

为什么不能new Set()?

因为Set是接口,不能被实例化。

为什么要用Set<E>而不是List<E>?

因为你需要去重的集合。

为什么用HashSet?

因为它是Set最常用的实现,性能好,且你不关心元素顺序。

Set<E> = new HashSet<>()是不是固定搭配?

是,除非你有顺序或排序的特殊需求。

2.解读 for(RequestContainer item: requestContainer) {}:

这个item根本没有被定义,直接使用不会报错吗?

在这段代码中,item是在for循环中隐式定义的,它是requestContainer列表中的每一个元素

具体来说,requestContainer是List<RequestContainer> 类型的集合,而RequestContainer是一个对象类型。

因此,item是指requestContainer列表中每次迭代时取出的一个RequestContainer 对象

看一下这一行:

for (RequestContainer item : requestContainer)

这里使用了增强的for循环(也叫for-each循环)。在 Java 中,增强的for循环的语法如下:

for (类型 变量名 : 集合/数组) {
    // 循环体
}

在这个例子中,RequestContainer item指定了循环中每个元素的类型和变量名。

item变量就代表了requestContainer列表中的每一个RequestContainer对象。

在每一次循环迭代时,item就会被赋值为requestContainer中的一个RequestContainer元素,直到列表遍历结束。

为什么不会报错?

item是由Java编译器隐式定义的变量,它在for循环内自动赋值。

只要requestContainer是一个合法的List<RequestContainer>,item就可以直接使用,不需要提前单独定义。

如果没有定义requestContainer或requestContainer的类型不是List<RequestContainer>,那肯定会报错。

这个item变量只有在requestContainer被正确初始化并包含了RequestContainer类型的元素时才会生效。

所以,item的定义是隐式的,是循环的一部分,并且它的作用域仅限于for循环内部。

3.这句增强for循环语句里,只有item是可以未曾事先定义的

✅ 增强for循环的语法:

for (类型 变量名 : 集合或数组) {
    // 循环体
}

🔹 各部分说明:

(1). 类型:

  • ✅ 必须是集合(Collection)或数组中元素的类型。
  • ❗必须是一个已经定义过的类型(可以是类、接口或基本类型)。
  • 会自动根据右边集合中的每个元素类型来匹配。

例如:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
for (String name : names) {
    System.out.println(name);  // name 是 String 类型
}

(2). 变量名:

  • 是临时变量名,不需要提前定义
  • 只在循环内部有效,相当于每次从集合/数组中取出来的元素临时放在这个变量里

你可以随便起名,只要有意义就好:

for (int score : scores) // score 是每次取出的元素

(3). 集合/数组:

  • ✅ 必须是一个已经存在且可以遍历的集合或数组。
  • ❗如果是集合,必须实现了Iterable接口(比如 List、Set、Queue 都行)。
  • ❗这个集合/数组变量必须已经赋过值,否则会报错或空指针。

例如:

int[] numbers = {1, 2, 3, 4};
for (int n : numbers) {
    System.out.println(n);
}

🔁 增强 for 循环做了什么?

其实它背后是自动帮你完成了这种结构:

for (int i = 0; i < arr.length; i++) {
    int n = arr[i];
    // do something
}

或者对于集合就是用迭代器:

for (Iterator<Type> it = collection.iterator(); it.hasNext(); ) {
    Type element = it.next();
    // do something
}

✅ 总结你说的三点:

类型需要提前定义?

✅ 是的

必须是集合/数组中的元素类型

变量名不需要提前定义?

✅ 是的

只在循环里起作用

集合/数组要预先定义?

✅ 是的

否则不知道从哪里取数据

4.其余代码的解读

(1) exCtnRequest.setIsSoc("SOC".equals(item.getSupplierType()) ? OrderConstants.YES : OrderConstants.NO);

exCtnRequest.setIsSoc("SOC".equals(item.getSupplierType()) ? OrderConstants.YES : OrderConstants.NO);

这行代码的目的是:根据item.getSupplierType()是否等于字符串"SOC",来设置exCtnRequest的isSoc字段的值。

如果 "SOC"==item.getSupplierType(),则 isSoc = OrderConstants.YES;

如果"SOC" !=item.getSupplierType(),则 isSoc = OrderConstants.NO;

什么是OrderConstants.YES,什么是OrderConstants.NO,怎么看起来怪怪的?

别忘了OrderConstants是一个常量类:

public class OrderConstants
{
	public static final String YES = "Y";
	public static final String NO = "N";
}

这回理解了吧,相当于就是 isSoc = "Y" || isSoc = "N"了。

(2) 其余

然后每个生成的ExCtnRequest对象会被添加到exCtnRequests集合中。

最后返回exCtnRequests:循环完成后,返回exCtnRequests集合,该集合包含了所有转换后的ExCtnRequest对象。

全部评论

相关推荐

07-08 13:48
门头沟学院 C++
点赞 评论 收藏
分享
每晚夜里独自颤抖:你cet6就cet6,cet4就cet4,你写个cet证书等是什么意思。专业技能快赶上项目行数,你做的这2个项目哪里能提现你有这么多技能呢
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
07-08 11:16
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务