通过EWS Java代码访问Outlook邮件时的字符引用解析错误问题
EWS Java API解析Outlook邮件字符引用错误解决方案
问题概述
使用Java通过EWS(Exchange Web Services)访问Outlook邮件时,部分邮件触发XML解析错误,错误信息:
ParseError at [row,col]:[852,6] Message: Character reference "&#
相关业务代码:
try { ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2); ExchangeCredentials credentials = new WebCredentials(EMAIL_ID, EMAIL_PWD); service.setCredentials(credentials); service.setUrl(new URI("https://*****************/ews/exchange.asmx")); service.setTraceEnabled(true); ExtendedPropertyDefinition PR_TRANSPORT_MESSAGE_HEADERS = new ExtendedPropertyDefinition(0x007D,MapiPropertyType.String); PropertySet propset = new PropertySet(PropertySet.FirstClassProperties.getBasePropertySet(),ItemSchema.MimeContent,PR_TRANSPORT_MESSAGE_HEADERS); propset.setRequestedBodyType(BodyType.Text); ItemView view = new ItemView(30); view.getOrderBy().add(ItemSchema.DateTimeReceived, SortDirection.Descending); FindItemsResults<Item> findResults; findResults = service.findItems(WellKnownFolderName.Inbox,"Body:"+currency, view); if (findResults.getTotalCount() == 0) { logger.info("INFO:: There is no emails available"); } else { for (Item item : findResults) { EmailMessage message = EmailMessage.bind(service, item.getId(), new PropertySet(BasePropertySet.FirstClassProperties, EmailMessageSchema.Attachments)); EmailMessage message = EmailMessage.bind(service, item.getId(), propset); item.load(); //emailBody = item.getBody().toString(); emailBody = message.getBody().toString(); logger.info("Email Body " + StringUtils.normalizeSpace(emailBody)); Date dateTimeReceived = item.getDateTimeReceived(); SimpleDateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy"); Date todays_date = new Date(); resultList.add(emailBody); } item.delete(DeleteMode.MoveToDeletedItems); } } catch (Exception e) { logger.info("Error in Executing outlookEmailRead : "+e.getMessage()); }
POM依赖:
<dependency> <groupId>com.microsoft.ews-java-api</groupId> <artifactId>ews-java-api</artifactId> <version>2.0</version> </dependency>
该问题为EWS Java API已知问题,以下是可行的解决方案:
解决方案
1. 直接获取MimeContent自行解析邮件内容
绕过EWS API自带的XML解析,直接获取邮件的MIME内容,使用JavaMail等工具解析文本:
// 修改PropertySet,只请求MimeContent PropertySet mimeProps = new PropertySet(ItemSchema.MimeContent); EmailMessage message = EmailMessage.bind(service, item.getId(), mimeProps); // 解析MIME内容 MimeContent mimeContent = message.getMimeContent(); ByteArrayInputStream inputStream = new ByteArrayInputStream(mimeContent.getContent()); MimeMessage mimeMsg = new MimeMessage(null, inputStream); // 提取文本内容 String emailBody = ""; if (mimeMsg.isMimeType("text/plain")) { emailBody = mimeMsg.getContent().toString(); } else if (mimeMsg.isMimeType("multipart/*")) { Multipart multipart = (Multipart) mimeMsg.getContent(); for (int i = 0; i < multipart.getCount(); i++) { BodyPart part = multipart.getBodyPart(i); if (part.isMimeType("text/plain")) { emailBody = part.getContent().toString(); break; } } }
2. 自定义XML解析器修复截断字符
通过自定义XML阅读器,在解析前修复不完整的字符引用(如截断的&#):
// 继承EWS的XmlReader,重写解析逻辑 public class FixInvalidXmlReader extends EwsXmlReader { public FixInvalidXmlReader(InputStream inputStream) throws XmlException { super(fixInvalidCharacters(inputStream)); } private static InputStream fixInvalidCharacters(InputStream inputStream) throws IOException { String xmlContent = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); // 修复截断的字符引用,将不完整的实体替换为空或合法格式 xmlContent = xmlContent.replaceAll("&#(?![0-9a-fA-F]+;)", ""); return new ByteArrayInputStream(xmlContent.getBytes(StandardCharsets.UTF_8)); } } // 替换EWS服务的XmlReaderFactory service.setXmlReaderFactory(new IXmlReaderFactory() { @Override public EwsXmlReader createXmlReader(InputStream inputStream) throws XmlException, IOException { return new FixInvalidXmlReader(inputStream); } });
3. 切换到社区维护的EWS API分支
原官方项目已归档,可使用社区维护的分支版本,该分支可能已修复此问题,修改POM依赖:
<dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>ews-java-api</artifactId> <version>2.0.1</version> </dependency>
4. 容错处理:捕获ParseError后重试加载
在catch块中针对XML解析错误单独处理,尝试使用不同的PropertySet加载邮件:
catch (XmlException e) { if (e.getMessage().contains("Character reference")) { // 尝试仅加载基础属性,跳过Body解析 PropertySet fallbackProps = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject, ItemSchema.DateTimeReceived); EmailMessage fallbackMsg = EmailMessage.bind(service, item.getId(), fallbackProps); logger.warn("邮件解析失败,仅加载基础信息:" + fallbackMsg.getSubject()); } }
内容的提问来源于stack exchange,提问作者Parthiban
相关产品推荐
相关产品推荐

