使用jaxb解析无效的xml - 解析器可以更宽松吗?

时间:2012-07-16 08:26:37

标签: xml parsing jaxb

我一直在使用JAXB一段时间来解析看起来像这样的xml:

<report>    <-- corresponds to a "wrapper" object that holds 
                some properties and two lists - a list of A's and list of B's
    <some tags with> general <info/>
    ...
    <A>   <-- corresponds to an "A" object with some properties
        <some tags with> info related to the <A> tag <bla/>
        ...
    <A/>
    <B>   <-- corresponds to an "B" object with some properties
        <some tags with> info related to the <B> tag <bla/>
        ...
    </B>
</report>

负责编组xml的方面很糟糕但不受我的控制 它经常发送无效的xml字符和/或格式错误的xml 我和负责人交谈并修复了很多错误,但有些错误似乎无法修复 我希望我的解析器尽可能地对这些错误感到宽恕,并且当它不可能时,从错误的xml中获取尽可能多的信息。
因此,如果xml包含100个A并且其中一个有问题,我仍然希望能够保留其他99个。 这些是我最常见的问题:

1. Some info tag inner value contains invalid chars
    <bla> invalid chars here, either control chars or just &>< </bla>
2. The root entity is missing a closing tag
    <report> ..... stuff here .... NO </report> at the end!
3. An inner entity (A/B)  is missing it's closing tag, or it's somehow malformed.
    <A> ...stuff here... <somethingMalformed_blabla_A/>
    OR
    <A> ...  Something malformed here...</A>

我希望我能很好地解释自己 我真的希望从这些xml中获取尽可能多的信息,即使它们有问题 我想我需要采用一些使用stax / sax和JAXB的策略,但我不确定如何。
如果100 A,一个A有xml问题,我不介意扔掉那个A. 虽然如果我能得到一个具有尽可能多的数据的A对象,可以解析直到错误,这会好得多。

2 个答案:

答案 0 :(得分:2)

XML的哲学是XML的创建者负责创建格式良好的XML,收件人不负责在到达时修复错误的XML。 XML解析器需要拒绝格式错误的XML。还有其他“整洁”的工具可以将错误的XML转换为良好的XML,但是根据输入中的缺陷的性质,它们的工作效果是不可预测的。如果您将获得使用XML进行数据交换的好处,那么它需要格式良好。否则你也可以使用自己的专有格式。

答案 1 :(得分:2)

这个答案真的帮助了我:

JAXB - unmarshal XML exception

就我而言,我正在使用XML开关(-x)解析Sysinternals Autoruns工具的结果。要么是因为结果被写入文件共享,要么是因为某些错误的原因,新版本中的XML会在接近结尾时出错。由于此Autoruns捕获对于恶意软件调查至关重要,因此我真的想要数据。另外,我可以从文件大小中看出结果几乎已经完成。

当您拥有OP建议的包含许多子元素的文档时,链接问题中的解决方案非常有效。特别是,Autoruns XML输出非常简单,由许多“项目”组成,每个“项目”由许多带文本的简单元素组成(即由XJC生成的字符串属性)。因此,如果最后遗漏了一些项目,没什么大不了的......除非它当然与恶意软件有关。 :)

这是我的代码:

public class Loader {

    private List<Exception> exceptions = new ArrayList<>();

    public synchronized List<Exception> getExceptions() {
        return new ArrayList<>(exceptions);
    }

    protected void setExceptions(List<Exception> exceptions) {
        this.exceptions = exceptions;
    }

    public synchronized Autoruns load(File file, boolean attemptRecovery)
      throws LoaderException {
        Unmarshaller unmarshaller;
        try {
            JAXBContext context = newInstance(Autoruns.class);
            unmarshaller = context.createUnmarshaller();
        } catch (JAXBException ex) {
            throw new LoaderException("Could not create unmarshaller.", ex);
        }
        try {
            return (Autoruns) unmarshaller.unmarshal(file);
        } catch (JAXBException ex) {
            if (!attemptRecovery) {
                throw new LoaderException(ex.getMessage(), ex);
            }
        }
        exceptions.clear();
        Autoruns autoruns = new Autoruns();
        XMLInputFactory inputFactory = XMLInputFactory.newInstance();
        try {
            XMLEventReader eventReader = 
              inputFactory.createXMLEventReader(new FileInputStream(file));
            while (eventReader.hasNext()) {
                XMLEvent event = eventReader.peek();
                if (event.isStartElement()) {
                    StartElement start = event.asStartElement();
                    if (start.getName().getLocalPart().equals("item")) {
                         // note the try should allow processing of elements
                         // after this item in the event it is malformed
                         try {
                            JAXBElement<Autoruns.Item> jax_b = 
                              unmarshaller.unmarshal(eventReader,
                                                     Autoruns.Item.class);
                            autoruns.getItem().add(jax_b.getValue());
                        } catch (JAXBException ex) {
                            exceptions.add(ex);
                        }
                    }
                }
                eventReader.next();
            }
        } catch (XMLStreamException | FileNotFoundException ex) {
            exceptions.add(ex);
        }
        return autoruns;
    }

    public static Autoruns load(Path path) throws JAXBException {
        return load(path.toFile());
    }

    public static Autoruns load(File file) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(Autoruns.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        return (Autoruns) unmarshaller.unmarshal(file);
    }

    public static class LoaderException extends Exception {

        public LoaderException(String message) {
            super(message);
        }

        public LoaderException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}