【问题标题】:JAXB filtered parsingJAXB 过滤解析
【发布时间】:2011-05-10 11:24:40
【问题描述】:

我正在使用 JAXB 在基于 GWT 的应用程序中解析 XML 文件。 XML 看起来像这样(一个简化的示例):

<addressbook>

    <company name="abc">
        <contact>
            <name>...</name>
            <address>...</address>
        </contact>

        <contact>
            <name>...</name>
            <address>...</address>
        </contact>

        <contact>
            <name>...</name>
            <address>...</address>
        </contact>
        ... 
        ... 
    </company>

    <company name="def">
        <contact>
            <name>...</name>
            <address>...</address>
        </contact>
        ...
        ...
    </company>

    ...
    ...

</addressbook>

我已经定义了如下所示的类:

@XmlRootElement(name="addressbook")
public class Addressbook implements Serializable {

    private ArrayList<Company> companyList = new ArrayList<Company>();

    public Addressbook() {            
    }

    @XmlElement(name = "company")
    public ArrayList<Company> getCompanyList() {
        return companyList;
    }


}

=============================

@XmlRootElement(name="company")
public class Company implements Serializable {

    private String name;

    private ArrayList<Contact> contactList = new ArrayList<Contact>();

    public Company() {      
    }

    @XmlAttribute
    public String getName() {
        return name;
    }

    @XmlElement(name = "contact")
    public ArrayList<Contact> getContactList() {
        return contactList;
    }

    ...
    ...
}

=============================

@XmlRootElement(name="contact")
public class Contact implements Serializable
{
    private String name;
    private String address;

    public Contact() {
    }

    @XmlElement
    public String getName ()
    {
        return name;
    }

    @XmlElement
    public String getAddress ()
    {
        return address;
    }

    ...
    ...
}

这是代码:

try {
    JAXBContext jc = JAXBContext.newInstance(Addressbook.class);
    Unmarshaller um = jc.createUnmarshaller();
    addressbook = (Addressbook) um.unmarshal(new FileReader("ds/addressbook.xml"));        
} catch (JAXBException e) {
    e.printStackTrace();
}

我需要根据公司名称获取联系人列表。例如,获取公司“abc”的所有联系人。我可以解析整个 XML 文件,然后手动过滤记录。但是如果输入文件很大,只解析我需要的文件可能会更有效。那么是否可以预先指定标准并仅解析特定记录?

谢谢。

【问题讨论】:

    标签: java xml jaxb


    【解决方案1】:

    您可以使用 EclipseLink JAXB (MOXy 中的 @XmlPath 扩展名来处理这种情况(我是 MOXy 技术负责人):

    @XmlRootElement(name="addressbook")
    public class Addressbook implements Serializable {
    
        private ArrayList<Company> companyList = new ArrayList<Company>();
    
        public Addressbook() {            
        }
    
        @XmlPath("company[@name='abc']")
        public ArrayList<Company> getCompanyList() {
            return companyList;
        }
    
    
    }
    

    更多信息:


    更新 - 使用 StreamFilter

    下面的示例演示了如何将 StreamFilter 用于此用例:

    import java.io.FileInputStream;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.Unmarshaller;
    import javax.xml.stream.XMLInputFactory;
    import javax.xml.stream.XMLStreamReader;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Addressbook.class);
    
            XMLInputFactory xif = XMLInputFactory.newFactory();
            FileInputStream xmlStream = new FileInputStream("input.xml");
            XMLStreamReader xsr = xif.createXMLStreamReader(xmlStream);
            xsr = xif.createFilteredReader(xsr, new CompanyFilter());
    
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            Addressbook addressbook = (Addressbook) unmarshaller.unmarshal(xsr);
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(addressbook, System.out);
        }
    }
    

    StreamFilter的实现如下:

    import javax.xml.stream.StreamFilter;
    import javax.xml.stream.XMLStreamReader;
    
    public class CompanyFilter implements StreamFilter {
    
        private boolean accept = true;
    
        public boolean accept(XMLStreamReader reader) {
            if(reader.isStartElement() && "company".equals(reader.getLocalName())) {
                accept = "abc".equals(reader.getAttributeValue(null, "name"));
            } else if(reader.isEndElement()) {
                boolean returnValue = accept;
                accept = true;
                return returnValue;
            }
            return accept;
        }
    
    }
    

    【讨论】:

    • 巧合的是,当您发布您的回复时,我正在阅读您的博客以获取想法。我认为这就是我正在寻找的东西,但如果可能的话,我真的更愿意为此避免额外的库。否则,我会考虑使用 MOXy。另一方面,我可以将它们解组为 Map,而不是解组 List 中的公司对象吗?
    • @DFB - 您可以将公司对象解组到地图 (bdoughan.blogspot.com/2010/09/…)。如果可以使用 StreamFilter 来获得您想要的行为(download.oracle.com/javase/6/docs/api/javax/xml/stream/…,使用标准 JAXB API。
    • 感谢您的回复。我将研究 StreamFilter。关于解组到地图中,我不得不承认我无法从您的博文中弄清楚。我想在我开始发布问题之前我还有很多东西要学:)
    • @DFB 我提供了指向地图示例的错误链接。正确的链接是:bdoughan.blogspot.com/2010/07/…
    • @DFB - 我已经用 StreamFilter 示例更新了我的答案,该示例应该适用于任何 JAXB 实现。
    【解决方案2】:

    你也可以

    • 对 XML 文件应用 XSLT 转换,或
    • 将文件解组为 DOM,并使用 XPath 选择所需的节点

    在将结果对象传递给解组方法之前

    不过,创建一个以公司名称为关键字的内存中 Map 可能更简单:

    public class SearchableAddressBook {
    
        public final Map<String, Company> companyMap = new HashMap<String,Company>();
    
        public SearchableAddressBook(List<Company> companyList) {
            for (Company company: companyList) {
                companyMap.add(company.getName(), company));
            }
    
    }
    

    如果您真的想要过度设计,也可以创建一个内存数据库。

    【讨论】:

    • 感谢您的回复。您能否使用这些方法中更简单的方法展示(或指向我)示例代码?抱歉,我还是新手。
    • 更新了我的答案,虽然 Map 方法仍然解析整个 XML 文件,所以它可能不是你要找的。记得测量各种数据集的性能!
    • 您是否建议通过修改我的 Addressbook 类,解组将数据放入 Map 中(实际上这很棒)?或者我应该创建一个新类 SearchableAddressBook 以将列表“在”解组后转换为地图?谢谢。
    • 不,恐怕您必须先解组,然后再添加到新的 SearchableAddressBook 中。我认为其他答案可能更合适:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2016-05-29
    • 2014-03-19
    • 2012-12-25
    • 2011-08-11
    相关资源
    最近更新 更多