diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataListConverter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataListConverter.java
new file mode 100644
index 000000000..0b55a9c03
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataListConverter.java
@@ -0,0 +1,54 @@
+package me.chanjar.weixin.common.util.xml;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+/**
+ * 兼容两种格式的字符串列表转换器:
+ *
+ * - 旧格式(4.8.0之前):<MemChangeList><![CDATA[id1,id2]]></MemChangeList>
+ * - 新格式(4.8.0起):<MemChangeList><Item><![CDATA[id1]]></Item></MemChangeList>
+ *
+ * 解析结果统一为逗号分隔的字符串。
+ */
+public class XStreamCDataListConverter implements Converter {
+
+ @Override
+ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
+ if (source != null) {
+ writer.setValue("");
+ }
+ }
+
+ @Override
+ public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+ if (reader.hasMoreChildren()) {
+ // 新格式:含有 - 子元素
+ StringBuilder sb = new StringBuilder();
+ while (reader.hasMoreChildren()) {
+ reader.moveDown();
+ String value = reader.getValue();
+ if (value != null && !value.isEmpty()) {
+ if (sb.length() > 0) {
+ sb.append(",");
+ }
+ sb.append(value);
+ }
+ reader.moveUp();
+ }
+ return sb.length() > 0 ? sb.toString() : null;
+ } else {
+ // 旧格式:直接 CDATA 文本
+ String value = reader.getValue();
+ return (value != null && !value.isEmpty()) ? value : null;
+ }
+ }
+
+ @Override
+ public boolean canConvert(Class type) {
+ return type == String.class;
+ }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
index 08a093631..6475623d8 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
@@ -11,6 +11,7 @@
import me.chanjar.weixin.common.util.xml.IntegerArrayConverter;
import me.chanjar.weixin.common.util.xml.LongArrayConverter;
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
+import me.chanjar.weixin.common.util.xml.XStreamCDataListConverter;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
@@ -156,7 +157,7 @@ public class WxCpXmlMessage implements Serializable {
private String memChangeCnt;
@XStreamAlias("MemChangeList")
- @XStreamConverter(value = XStreamCDataConverter.class)
+ @XStreamConverter(value = XStreamCDataListConverter.class)
private String memChangeList;
@XStreamAlias("LastMemVer")
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
index 0b2324a5f..e87ff2334 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
@@ -570,5 +570,53 @@ public void testExternalChatChangeEvent() {
assertEquals(wxMessage3.getUpdateDetail(), "change_name");
// 当XML中没有MemChangeList元素时,字段应该为null而不是空字符串
assertThat(wxMessage3.getMemChangeList()).isNull();
+
+ // 测试企业微信4.8.0新格式:MemChangeList使用
- 子元素(加群场景)
+ String xmlNewFormatAddMember = ""
+ + ""
+ + ""
+ + "9811170016713"
+ + ""
+ + ""
+ + ""
+ + ""
+ + ""
+ + "3"
+ + "1"
+ + "
"
+ + ""
+ + ""
+ + "";
+ WxCpXmlMessage wxMessage4 = WxCpXmlMessage.fromXml(xmlNewFormatAddMember);
+ assertEquals(wxMessage4.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CHAT);
+ assertEquals(wxMessage4.getChangeType(), "update");
+ assertEquals(wxMessage4.getUpdateDetail(), "add_member");
+ assertEquals(wxMessage4.getJoinScene(), "3");
+ assertEquals(wxMessage4.getMemChangeCnt(), "1");
+ // 新格式:- 子元素中的成员ID应被正确解析
+ assertEquals(wxMessage4.getMemChangeList(), "wmxUBwDQAAO-Hn5_wFJz4wvo5TxLFibw");
+
+ // 测试企业微信4.8.0新格式:多个
- 子元素(多成员变更)
+ String xmlNewFormatMultiMember = ""
+ + ""
+ + ""
+ + "1403610513"
+ + ""
+ + ""
+ + ""
+ + ""
+ + ""
+ + "1"
+ + "2"
+ + ""
+ + "
"
+ + " "
+ + ""
+ + "";
+ WxCpXmlMessage wxMessage5 = WxCpXmlMessage.fromXml(xmlNewFormatMultiMember);
+ assertEquals(wxMessage5.getUpdateDetail(), "del_member");
+ assertEquals(wxMessage5.getMemChangeCnt(), "2");
+ // 多个- 元素应被解析为逗号分隔字符串
+ assertEquals(wxMessage5.getMemChangeList(), "wmEJiCwAAA9KG2qlSq6rKwASSgAAAA,wmEJiCwAAA9KG2qlSq6rKwBBBBBBB");
}
}