DEX455

JSON、XML、ZIP、加密与正则表达式完全指南

课程介绍

JSON/XML/ZIP/Crypto/Pattern完全指南:JSON(JSON.serialize+deserialize+deserializeStrict+deserializeUntyped/Map键类型限制11种/API28.0+null不序列化+额外字段序列化/API63.0+异常不序列化)、JSONGenerator(writeStartObject/字段/数组/prettyPrint)、JSONParser(JSONToken枚举10种/readValueAs/skipChildren)、XmlStreamReader(前向只读50节点深/hasNext+next/事件类型7种)、XmlStreamWriter(命名空间+属性+CData/空元素)、DOM(Document+XmlNode/createRootElement+addChildElement/getChildElements/XML命名空间/元素vs节点区别)、ZIP(Compression/ZipReader/getEntry+extractEntry)、Crypto(generateDigest+generateMac+sign+encryptWithManagedIV+decryptWithManagedIV+generateAesKey/AWS HMacSHA1集成)、EncodingUtil(urlEncode+base64Encode+convertToHex/HTTP Digest认证)、Pattern+Matcher(compile+matcher/matches+lookingAt+find/start+end+group/region+anchoring+transparent bounds/捕获组编号/1M访问限制/邮件正则化示例)。

课程章节

本课程共有 11 个章节

  • 1

    JSON, XML, ZIP, Crypto, Encoding & Patterns

    第 416 页

    同学们,今天我们来聊聊Apex里的一群得力助手——实用程序类。它们就像工具箱里的常用工具,帮我们处理数据交换、安全、压缩和文本这些日常任务。 首先,是处理JSON的类。现在大部分系统之间交换数据都用JSON格式,Apex直接内置了序列化和反序列化的方法,就是说你可以轻松地把一个对象转成JSON字符串发送出去,或者把收到的JSON字符串转回对象来使用,非常方便。 接着,还有处理XML的类。有时候我们需要读写XML文档,Apex提供了两种方式:流和DOM。如果你要处理很大的XML文件,可以用流,一边读一边处理,不用把整个文件加载到内存。而DOM呢,是把整个XML解析成树状结构,方便你来回查找和修改节点。 然后,我们说说压缩和解压缩。Apex自带Zip处理的类,你可以把多个文件打包成一个Zip,或者从Zip里提取文件。比如你要生成一份报告,把相关的文件压缩在一起,就可以用这个功能。 再来看安全方面,Apex支持与AWS集成,用加密来保护数据安全。你可以调用AWS的密钥管理服务,对敏感数据进行加密解密,确保数据在传输和存储中都安全。 日常开发中,经常要处理URL,拼接链接或者拿到URL里的参数,这时候URL编码和解码的类就上场了。比如你要把用户输入的文本变成URL安全的字符串,或者还原回来。 最后是正则表达式的类,用来做文本搜索和匹配。你可以用模式来验证邮箱格式、手机号码,或者从大段文字里提取出你想要的信息。 总结一下,这些实用类就是Apex提供的基础能力封装,让我们的开发更高效、更安全。无论是数据交换、文件压缩、安全加密还是文本处理,它们都帮你做好了准备,只要按需调用就好。 好了,这节课就到这里,下次我们再深入看看每个类具体怎么用。

    查看详情
  • 2

    JSON Support — Overview & Classes

    第 417 页

    同学们,我们来看这一页,它讲的是Apex里面处理JSON最核心的几个工具。简单说,Apex给了我们三个主要的类来跟JSON打交道,你可以根据场景灵活选择。 第一个是System.JSON,它是最常用的,提供了高级序列化和反序列化操作。什么叫序列化呢?就是把Apex里的对象、集合这些数据,直接转成JSON字符串。反过来,把一段JSON字符串转回Apex对象,就叫反序列化。用System.JSON,一行代码就搞定了,非常适合大多数情况。 第二个是JSONGenerator,这个类呢,是让你像写文章一样,一步一步“写”出JSON。你可以先用writeStartObject开始一个对象,再用writeStringField写字段,最后writeEndObject结束。这种增量构建的方式,在你需要精细控制输出、或者数据是边算边生成的场景下特别好用。 第三个是JSONParser,它和JSONGenerator正好相反,是读取用的。它不是一次性把整个JSON解析成对象,而是像读流一样,一个令牌一个令牌地读,比如遇到一个左花括号、一个字段名、一个值。这种方式适合解析特别大的JSON,或者你需要逐个检查内容、做特殊处理的情况。 那在解析过程中,可能遇到的每种元素,比如字段名、数字值、布尔值、空值等等,都对应着一种令牌类型。Apex用了一个JSONToken枚举来列举所有这些可能的类型,方便你在遍历的时候做判断。 当然,无论是序列化还是解析,如果格式不对,比如少了个引号,或者结构不合法,这三个类的方法都会抛出异常,这个异常叫JSONException。所以写代码时记得做好异常捕获。 最后我们提一个方便的功能:System.JSON里面还有一个serializePretty方法。它和普通的serialize一样,也是把对象转成JSON,但输出结果是带有缩进和换行的,我们管它叫人类可读的格式。调试或者展示JSON的时候用它,看起来就特别清晰。 以上就是这页的核心内容,三个类各司其职,再加上令牌枚举和异常处理,构成了Apex中完整而灵活的JSON工具集。大家先有个轮廓印象,后面练习时我们还会深入用起来。

    查看详情
  • 3

    JSON — Serialization/Deserialization Considerations

    第 418 页

    同学们,咱们今天来看看Apex里一个很实用的功能——SON序列化。说白了,序列化就是把一个对象转成一串J-S-O-N格式的文本,方便我们在系统之间传数据,或者记日志,又或者在前端处理。 首先,Apex的SON序列化支持哪几类东西呢? 它很强大,普通的数据类型,比如字符串、数字、日期,还有集合里的列表、集合,都能序列化。最常用的是sObject,像Account、Contact这些标准或自定义的对象,直接就能转成JSON。还有数据库查询返回的结果容器,比如单个sObject或者列表,也支持。更棒的是,你完全可以自己写一个Apex类,里面定义几个属性,它也能被序列化,这样你就能把复杂的结构化数据打包成JSON发出去。 但是,这里面有几个坑得小心。 第一个限制:托管包里的Apex类实例,如果从包外面想要序列化它,对不起,不行。这是出于安全考虑,防止外部代码偷窥包里的数据结构。 第二个,如果你序列化一个Map,它对键的类型要求很严格,只能是那些简单的、可以直接排序的类型,像布尔值、日期、时间等等,复杂对象比如sObject绝对不行。 第三个,父子关系的问题很有意思。比如一个Account下面有Contacts,你把Account作为父对象序列化,它内嵌的子列表里每个Contact对象却会丢掉那些只在子类型里才有的字段。它只会保留Contact和Account共有的那部分属性,有点坑人。 还有,如果你试图序列化一个自引用的对象图,比如一个节点自己指向自己,Apex会抛出一个JSONResponse异常来保护你,阻止无限递归。即便你强行走通了,序列化出来的引用图其实是拷贝,不再是原对象引用关系。 最后,JSONParser这个类本身是不能被序列化的,你得用它来解析JSON,而不是把它打包进去。 再讲几个版本变迁中的行为差异,因为不同API版本编译的代码表现会不一样: 从API 63.0开始,如果代码执行中抛出了异常,你在序列化时不会把这个异常对象也转成JSON——它直接阻止这么做,这是为了避免敏感信息泄漏。 更早的API 53.0修复了一个日期时间精度的问题,之前可能损失毫秒,现在能精准保留了。 如果你用API 28.0以上版本,查询出的sObject在序列化时会自动带上关联查询的外键字段,但请注意,值为空的字段是不会出现的,这是为了减小JSON体积。 而API 34.0以下的版本里,有一个历史遗留问题:把一个序列化后的JSON再反序列化回来,对象的ID比较可能会失败,因为ID的内部结构不一样了。用新版本就没这烦恼。 记住这些规则,你在写集成或调试的时候就能少走很多弯路。好了,今天就说到这儿,大家可以在练习项目里动手试试,感受一下JSON序列化的方便与限制。

    查看详情
  • 4

    JSONGenerator — Incremental JSON Creation

    第 419 页

    大家好,今天我们来聊聊 Apex 里一个非常方便的类——JSONGenerator。很多时候我们需要在代码里生成 JSON 数据,比如要传给外部系统,或者构造一个响应。如果手动拼接字符串,很容易出错,而且代码不美观。JSONGenerator 就提供了很优雅的增量式构建方式。 首先,我们怎么创建它呢?就是用 JSON.createGenerator,后面可以传一个布尔参数 true。这个 true 的作用是开启“漂亮打印”,让生成的 JSON 自动带缩进和换行,阅读起来特别清晰。如果传 false 或者不传,那就是紧凑格式。创建好以后,我们就可以一步步往里写内容了。 写入的时候,就像在搭积木:用一个 writeStartObject 表示一个大括号开始,这是对象的边界;写完里面的内容后,再用 writeEndObject 把这个对象关上。如果字段名需要写,就用 writeFieldName 指定名字。然后,我们可以用一系列类型化的写方法,比如 writeNumberField、writeStringField、writeBooleanField,甚至是 writeNullField,这些方法直接接收字段名和值,一步到位,省得你先写字段名再写值。还有一个 writeObjectField,可以把你一个字段名和一个 Apex 对象一起传进去,它会自动把对象序列化放到这个字段下面。 那如果要写整个 Apex 对象呢?还有一个更厉害的方法叫 writeObject,直接把一个 Apex 实例传给它,它就会把对象的所有可序列化属性展开成一个 JSON 对象,嵌套在当前位置。这对于把 Salesforce 记录输出成 JSON 特别便捷。 最后,等全部内容写入完毕,调用 getAsString 方法,就能拿到完整的 JSON 字符串了。 为了让你更直观地理解,我描述一下我们课堂上演示的例子:我们构建了一个嵌套的 JSON 结构,里面有一个人的基本信息,比如姓名和年龄;然后有一个地址,这是一个嵌套对象,包含了城市和街道;还有一个爱好列表,是一个数组,里面放了几项字符串;最精彩的是,我们还直接序列化了一个自定义 Apex 对象,比如一个 Account 记录,它被整个放到了 “account” 字段下面。所有这些内容,都集中在一个 JSON 结构里,清晰得很。通过这个例子,你就能看到 JSONGenerator 处理复杂层级是多么游刃有余。 总结一下,JSONGenerator 就是用增量、类型安全的方式来生成 JSON,避免手动拼接,支持嵌套对象、数组,以及任何 Apex 对象的直接序列化。希望今天的讲解能帮你轻松上手,以后构建 JSON 就用它吧。

    查看详情
  • 5

    JSONParser — Token Streaming & Object Parsing

    第 420 页

    好,同学们,咱们今天来看一看 Apex 里怎么解析 JSON 数据。JSON 这种格式大家现在应该不陌生了,前后端交互、调用外部 API,响应回来的很多都是 JSON。Salesforce 给咱们提供了一个特别好用的工具,叫 JSONParser。 这个 JSONParser 呢,主要支持两种解析思路,你可以根据场景来选。 第一种,我叫它“逐个令牌”的方式,也就是 token-by-token。怎么做呢?你先用 nextToken() 方法来初始化解析器,它会指向 JSON 里的第一个令牌。然后你可以在循环里,根据 JSONToken 这个枚举,检查当前令牌是什么类型——比如是不是字段名、是不是值、是不是一个数组的开始。用 getCurrentToken() 一看就知道了。如果当前令牌正好是字段名,那咱们再用 getText() 把字段名和它对应的值提取出来。这种方式很灵活,特别适合处理那些字段顺序可能不确定、或者你只需要部分数据的情况。而且它对字段名称的匹配也做得非常优雅,你代码写起来会很清爽。 第二种方法更直接,叫 readValueAs。你只要给它一个目标类型,它就能把一整个 JSON 对象直接反序列化成一个 Apex 类的实例。比如你有一个定义好属性的类,结构跟要解析的 JSON 对象正好对得上,那你一行代码就能把 JSON 变成对象。但前提是类的结构必须和 JSON 完全匹配,字段名、层级都得对应好,不然会报错。 咱们再讲一个很实用的小技巧:skipChildren()。当你在遍历一个 JSON 数组,读完一个完整的对象之后,如果这个对象里面还嵌套着子数组或者子对象,你不想一层层手动解析,skipChildren() 就能帮你直接跳过这些子元素,让你快速定位到下一个同级元素,这在处理对象数组的时候特别省事。 最后我举一个例子。假设你调用了一个收件箱相关的 API,返回的 JSON 里有很多版本信息。要求你从这个响应里提取出 API 版本号,构建出一个从版本号到发布标签的 Map。那你就可以用 token-by-token 的方式,定位到版本号和标签字段,灵活地把数据塞进 Map 里。或者如果你定义好了对应的 Apex 类,直接用 readValueAs 把整个响应映射成对象也挺省心。 所以,掌握好这两种方法,再加上 skipChildren() 的灵活运用,你处理绝大多数的 JSON 解析场景都会游刃有余。好,这部分就讲到这儿,大家有什么问题吗?

    查看详情
  • 6

    XML Support — Reading & Writing Streams

    第 421 页

    同学们,今天我们来看看在Salesforce Apex里怎么用流的方式去读写XML。你平常处理数据的时候,遇到XML结构不想一股脑全装进内存,就可以用这两个类:XmlStreamReader和XmlStreamWriter。 先讲XmlStreamReader,它就像一个只能向前翻页的电纸书,你只能一页一页往后读,不能回头,这就是“前向只读”。它通过事件来告诉你读到了什么,比如START_ELEMENT表示一个标签开始了,END_ELEMENT表示标签结束,CHARACTERS表示遇到了一段文本,还有ATTRIBUTE这些事件。你在用的时候一定要养成一个好习惯:每次调用next()跳到下一个事件之前,先用hasNext()检查一下是不是已经读到结尾了,不然就越界报错,就像翻书翻过最后一页一样。要拿到标签的名字,我们用getLocalName();如果想取属性值,就调getAttributeValue();遇到元素里面的文字内容,就用getText()把它们拿出来。这些方法都很直接,一看名字就知道是做什么的。 再说XmlStreamWriter,它正好相反,是用来“盖房子”,一步一步把XML结构搭出来。你得按顺序来:先writeStartDocument,表示文档开始;然后如果用到命名空间,就用带命名空间的writeStartElement写开始标签;接着如果有属性就writeAttribute,有文字内容就writeCharacters;最后别忘了writeEndElement结束这个标签,以及整个文档结束的writeEndDocument。这样一套流程走下来,一个完整的XML就生成好了。这个类还支持写处理指令、DTD、注释,甚至可以直接写空元素。 最后记住一个小限制:Apex的流式XML处理最多只能支持50层节点的嵌套,如果你写的XML层层套得太深,超过50层就会报错,所以在设计结构时要留心。 好啦,这就是XmlStreamReader和XmlStreamWriter的基本用法,理解了事件的顺序和这些方法的职责,你就能轻松地在Apex里流式处理XML了。

    查看详情
  • 7

    XML Support — DOM Parsing

    第 422 页

    我们这节课来聊聊 Apex 里一个很实用的工具——Dom 类,全称是 Document Object Model,用它可以解析和生成完整的 HTML 或者 XML 文档。这在做接口集成、处理 HTTP 响应时特别有用。 我们先看怎么解析。比如你通过 HTTP 请求拿回了一段 HTML,怎么把它变成程序能懂的结构呢?你可以用 `getBodyDocument()` 方法,直接从响应里提取出文档对象,这就是整棵节点树的入口。拿到文档后,你会得到一个根元素,比如 `<html>`,然后就可以通过它的名字,或者带命名空间的方式,一层层访问子元素了。注意,标签之间的文本在 DOM 里并不是直接挂在元素上的,而是一个单独的文本节点,你要拿文本内容的话,需要用相应的方法把它取出来。 再说创建。如果你想自己拼装一个 HTML 文档,比如要作为 HTTP 请求的 body 发出去,就得用构造器新建一个 `Dom.Document` 对象,然后创建根元素。这里有个小细节,如果涉及命名空间,建根元素的时候最好带上命名空间的 URI 和前缀,这样能保证唯一性。接着,你可以用 `addChildElement()` 方法给它加子元素,一层套一层,形成嵌套结构。想放文字内容的话,就用 `addTextNode()` 来添加一个文本节点。整个文档建好之后,可以通过 `setBodyDocument()` 把它塞到一个 HTTP 请求里,再发出去。 这里我再强调一下节点和元素的区别:元素是那些带尖括号的标签,比如 `<p>`,而它里面的文字是另一个节点,叫文本节点。它们共同构成一棵树,解析或生成的时候要分清这两种节点。 另外,Apex 里对多层嵌套的支持是有限制的,最多支持 50 层深度的嵌入式标记。也就是说,你不能无限地 `<div>` 套 `<div>`,超过 50 层就会报错,在设计结构时要注意这一点。 好了,这就是今天的内容,Dom 类让我们能直接操作 HTML 文档结构,非常灵活。你可以试着用它处理一些简单的 XML 解析或者动态生成 HTML 的场景,会很有帮助。

    查看详情
  • 8

    ZIP Support & Crypto — Securing Data

    第 423 页

    同学们,今天我们来聊聊Apex里处理压缩和加密的两个工具,分别是 Compression 命名空间和 Crypto 类。别担心,它们听起来复杂,其实用起来很直接。 先看压缩,就是那个 ZIP 文件。很多时候我们接收到的数据是打包好的 ZIP 存档,如果只想拿里面某一个文件,难道要把整个包都解压吗?不用。Apex 提供了 ZipReader 类,你可以把它想象成一个能直接阅读 ZIP 文件的读卡器。我们把 ZIP 文件内容作为 Blob 数据交给 ZipReader,然后调用 getEntry 方法,写下你想找的文件名,它就能定位到那个具体的文件。再调用 extractEntry 方法,就能把那个文件的原始数据取出来,剩下的文件根本不用动,既省时间又省资源。记住,三个步骤:用 Blob 创建 ZipReader,getEntry 找到文件,extractEntry 取出数据。 接下来是加密相关的 Crypto 类。它把几种安全算法都准备好了,我们直接调用就行。第一类是生成摘要,也叫做散列,可以用来验证数据有没有被篡改。用 generateDigest 方法,指定算法,比如 SHA1、SHA256 或者 MD5,传入原始数据,就能得到一个独一无二的指纹串。第二类是消息认证码,也就是 HMAC,在散列的基础上加了一把钥匙,既验证完整性又验证来源。用 generateMac 方法,算法可以选 HMacSHA1 或 HMacSHA256,同时提供密钥和数据,它就会算出认证码。第三类是对称加密 AES,用来保护数据的机密性。加密用 encryptWithManagedIV,解密用 decryptWithManagedIV,支持 128、192 和 256 位的密钥长度,它会自动帮你处理初始化向量,让操作更安全也更简单。 举个实际场景:AWS 集成的时候,我们需要对请求进行签名,用的就是 HMacSHA1。拿请求里的关键信息和密钥一起算出一个 HMAC 签名,放到请求头里,AWS 那边就能凭同样的算法验证是你发送的请求,没被篡改过。 最后提醒一下,在做单元测试的时候,一定要验证加密和解密能不能正确往返——也就是加密后再解密,应该得到原始数据。同时还要故意传一个错误的密钥长度,看看系统会不会抛出 SecurityException,这样就能确保我们的代码对密钥长度有正确的校验。 好了,今天关于压缩和加密的工具就讲到这里。大家可以在开发者文档里多看看这些方法的示例,动手试一试。

    查看详情
  • 9

    Encoding Utilities

    第 424 页

    同学们,咱们今天聊聊Apex里一个非常实用的小工具类,叫EncodingUtil。你别看它名字里带着“编码”,就觉得难懂,其实它特别简单,就是帮我们把数据转换成各种各样的格式,方便我们和外部系统打交道。 那它到底能做什么呢?主要有三类操作。 第一类,URL编码和解码。你上网的时候,网址里有些特殊字符,比如空格、问号、等于号,如果直接放在URL里,可能会被误解。这时候,我们就需要用“URL编码”把它们变成安全的格式,比如空格变成%20。在Apex里,如果你想对一个查询参数进行编码,比如要传一个时间戳,你就可以用EncodingUtil.urlEncode,指定编码方式,比如UTF-8,轻松搞定。 第二类,Base64编码和解码。Base64是一种把二进制数据转成纯文本的方式,在网络传输中特别常用。比如,你跟AWS服务集成,需要给请求头加一个HMAC签名,这个签名通常就是二进制数据,但HTTP头只能放文本,怎么办呢?直接用EncodingUtil.base64Encode,把签名转成Base64字符串,就可以放进Authorization头里了。反过来,收到Base64数据,也可以用base64Decode解码回原来的二进制。 第三类,十六进制转换。有时候我们需要把数据变成十六进制字符串,比如在实现HTTP Digest身份验证时,根据RFC 2617规范,我们需要计算SHA-1哈希值,然后把它转成十六进制摘要。EncodingUtil的convertToHex方法就能直接把二进制哈希值转成十六进制字符串,非常方便。当然,也有convertFromHex,把十六进制转回二进制。 其实总结下来,EncodingUtil这个类就是专门用来处理这些“转换杂务”的,尤其当你的代码需要调用外部Web服务,要传编码参数、身份验证凭证时,这些方法简直是救命稻草。你不用自己去写复杂的编码逻辑,直接用现成的,安全又高效。 好了,关于EncodingUtil咱们就讲这么多。下一节我们来看看怎么用这些方法和外部API做具体的集成。有什么问题可以随时打断我。

    查看详情
  • 10

    Patterns and Matchers — Regular Expressions

    第 425 页

    同学们,今天我们来讲一下Apex里的正则表达式,其实就是模式匹配。这个知识点呢,是直接从Java的正则表达式照搬过来的,所以如果你用过Java,就很容易上手。 首先,我们要用到一个叫Pattern的类。怎么创建一个Pattern呢?很简单,用Pattern.compile方法,把你想匹配的正则表达式作为字符串传进去,就得到一个编译好的模式对象了。有了它之后,再用这个模式的matcher方法,把你想要搜索的字符串,也就是输入,传进去,就会得到一个Matcher对象。 这个Matcher对象就是我们的匹配器,它有三种最常用的匹配方式: 第一种叫matches,它会检查整个输入字符串,是不是完完全全符合我们定义的模式,从开头到结尾,一个字符都不能多。 第二种叫lookingAt,它只检查输入的开头部分,是不是匹配这个模式,后面有没有多余字符都没关系。 第三种叫find,它会在整个输入里反复扫描,找到每一个匹配的子串,就像拿一个探照灯,逐个点亮匹配的位置。 当你用find或者lookingAt找到匹配后,就可以用三个方法提取详细信息:start方法返回匹配到的子串的起始索引,end方法返回结束索引,group方法返回匹配到的文本内容。 另外,Matcher还支持区域限制。你可以给匹配器指定一个输入字符串的子区域,只在这个区域内搜索,外边的部分就自动忽略了。这样做可以提高效率,也能精准控制匹配范围。 说到区域边界,还有一个边界行为的概念。排队边界就是默认的——在区域的起始和结束位置,像一堵墙一样,不能跨边界匹配。但你可以设置成透明的边界,这样就允许匹配时查看区域外边的字符,方便某些需要上下文的正则条件。 最后,我们再简单说一下捕获组。正则表达式里用小括号包起来的部分就是捕获组。组的编号是从左到右,根据左括号出现的顺序来数的。特别注意,组0总是代表整个表达式匹配到的全部内容,第一个左括号是组1,依次类推。 好了,今天关于Apex中的Pattern和Matchers就讲到这里。记住这几个操作和方法,就足够应付日常的正则匹配需求了。大家下课!

    查看详情
  • 11

    Pattern & Matcher — Complete Example

    第 426 页

    今天我们来聊聊 Apex 里处理电子邮件的一个实用例子——怎么用正则表达式把乱七八糟的邮箱地址整理成统一的格式,并且还能找出重复的记录。这个例子虽然不长,但里头的门道不少,我们一步步拆开来讲。 首先,想象一下,有些客户填写的公司邮箱可能带着各种奇怪的子域名,比如 john@*.smithco.com,这种格式其实不太标准。我们想要把这种地址简化成 john@smithco.com。怎么做到呢?就用到了带有,命名捕获组,的正则表达式。 这个正则大致会分成两部分:一个是用户名,一个是域名。我们用命名捕获组把这两块分别抓出来,比如给用户名起名叫 `user`,域名叫 `domain`。然后,在匹配的过程中,我们还能对捕获到的域名部分做进一步的清洗,去掉那些多余的星号、点号之类的,只保留 `smithco.com`。 具体代码里,我们会调用 `matcher.replaceAll('')` 这样一个方法。它的作用是把每个匹配到的邮箱地址替换成我们规范化后的格式。这里头其实隐含了一个逻辑:如果你在替换的字符串里引用命名捕获组,就可以重新组合出干净的邮箱。 好,规范化这一步做完了,接下来要在批处理里检测重复的邮箱。这里的“批内”是指我们可能一次性处理一批数据,比如几百条联系人记录。为了防止同一批数据里有重复的规范化邮箱地址,我们可以建一个 Map,Key 就是规范化后的邮箱,Value 是记录本身。这样,当发现 Key 已经存在的时候,就知道遇到了重复,可以进行相应的处理,比如标记或者跳过。 相比批内去重,更关键的是要和数据库里已有的数据比对。Apex 里通常会有一个自定义字段,比如叫 `duplicate_key__c`,专门存储规范化后的邮箱。这样我们就可以直接用 SOQL 查询:“找出所有 `duplicate_key__c` 等于这个新邮箱的现有记录”。如果查到了,就说明老记录已经存在,我们就能做出防重逻辑。 以上是实际业务里和 Salesforce 平台结合的做法。现在再来看一个更基础的正则匹配小例子,帮助大家理解 `Matcher` 的一些方法。 假设我们用一个简单的正则,去匹配一个字符串,并且带了一个捕获组。例如,匹配字母 “a”,并且把它捕获。当你调用 `matcher.find()` 之后,可以用 `matcher.group(0)` 得到整个匹配到的字符串,用 `matcher.group(1)` 得到第一个捕获组的内容。这里注意,`group(0)` 总是代表整个匹配,不管你有没有显式的括号。 还有一个 `end()` 方法,它返回的是最后一个匹配字符后面的那个位置的索引偏移量。这个索引是从 0 开始数的,所以如果整个匹配占了字符串的前几个字符,`end()` 得到的数字其实就是匹配部分的长度,因为它是最后一个字符后面的位置。比如匹配到的部分是从索引 0 到 2,那 `end()` 就是 3。 理解这些小细节,对于后面编写更复杂的正则替换逻辑非常有帮助。 总结一下这一页 Slide 的核心:我们学会了用正则表达式抓取邮箱的用户和域,清洗域名,再用 `replaceAll` 格式化。同时,在批处理内部用 Map 去重,并且利用数据库字段做跨记录比对,防止数据重复。最后还温习了 Matcher 里的 `group` 和 `end` 方法,为实战打下基础。 这样讲,是不是清晰多了?有疑问随时提。

    查看详情