2015-04-05 2 views
2

Я хотел бы взять XML-файл, сильно структурированный и примерно половинный гигабайт, и создать из него еще один XML-файл, содержащий только выбранные элементы исходного.Создание XML на основе другого XML в Java

1) Как я могу это сделать?

2) можно ли это сделать с помощью DOM Parser? Каков максимальный размер анализатора DOM?

Спасибо!

+0

Рассмотрите возможность использования XSLT, который позволяет вам писать шаблон (в XML), который действует как рецепт для извлечения элементов и/или атрибутов, которые вы хотите, а затем записывать их как новый документ (в случае необходимости XML). Я использовал [Saxon] (http://saxon.sourceforge.net/) для этого в прошлом (используя скрипт командной строки, а не приложение Java). – Bobulous

+0

Возможно, вы захотите прочитать файл последовательно, сохранив только те элементы, которые вам действительно нужны. С помощью этой стратегии вам не нужно будет выделять память для хранения и управления вашим 0,5 ГБ файлом. Вы можете сделать это с помощью анализатора SAX. Вы также можете использовать Stax в Java. – helderdarocha

ответ

2

Если у вас очень большой исходный XML-файл (например, ваш файл объемом 0,5 ГБ) и вы хотите извлечь из него информацию, возможно, создав новый XML, вы можете подумать об использовании анализатора на основе событий, который не требует загрузки всего XML в памяти. Простейшей из этих реализаций является синтаксический анализатор SAX, который требует, чтобы вы записывали прослушиватель событий, который будет захватывать такие события, как начало документа, начало элемента, конец элемента и т. Д., Где вы можете проверять данные, которые вы читаете (имя элемент, атрибуты и т. д.) и решить, будете ли вы игнорировать его или делать что-то с данными.

Поиск учебника SAX с использованием JAXP, и вы должны найти несколько примеров. Другая стратегия, которую вы можете рассмотреть, в зависимости от того, что вы хотите сделать, - это StAX.

Вот простой пример использования SAX для чтения данных из файла XML и извлечения некоторой информации на основе критериев поиска. Это очень простой пример, который я использую для обучения SAX. Я думаю, это может помочь вам понять, как это работает. Критерии поиска жестко связаны и состоят из имен режиссеров фильмов для поиска в гигантском XML с выбором фильма, созданным из данных IMDB.

XML Source пример ("source.xml" ~ 300MB файл)

<Movies> 
    ... 
    <Movie> 
     <Imdb>tt1527186</Imdb> 
     <Title>Melancholia</Title> 
     <Director>Lars von Trier</Director> 
     <Year>2011</Year> 
     <Duration>136</Duration> 
    </Movie> 
    <Movie> 
     <Imdb>tt0060390</Imdb> 
     <Title>Fahrenheit 451</Title> 
     <Director>François Truffaut</Director> 
     <Year>1966</Year> 
     <Duration>112</Duration> 
    </Movie> 
    <Movie> 
     <Imdb>tt0062622</Imdb> 
     <Title>2001: A Space Odyssey</Title> 
     <Director>Stanley Kubrick</Director> 
     <Year>1968</Year> 
     <Duration>160</Duration> 
    </Movie> 
    ... 
</Movies> 

Вот пример обработчика событий. Он выбирает элементы Movie, сопоставляя строки. Я продлил DefaultHandler и реализовал startElement() (называется, когда найден тэг открытия), characters() (вызывается, когда считывается блок символов), endElement() (называется, когда найден конечный тег) и endDocument() (один раз, когда документ завершен). Поскольку считываемые данные не сохраняются в памяти, вам необходимо сохранить интересующие вас данные. Я использовал некоторые булевы флаги и переменные экземпляра, чтобы сохранить текущую метку, текущие данные и т.д.

class ExtractMovieSaxHandler extends DefaultHandler { 

    // These are some parameters for the search which will select 
    // the subtrees (they will receive data when we set up the parser) 
    private String tagToMatch; 
    private String tagContents; // OR match 
    private boolean strict = false; // if strict matches will be exact 

    /** 
    * Sets criteria to select and copy Movie elements from source XML. 
    * 
    * @param tagToMatch Must contain text only 
    * @param tagContents Text contents of the tag 
    * @param strict If true, match must be exact 
    */ 
    public void setSearchCriteria(String tagToMatch, String tagContents, boolean strict) { 
     this.tagToMatch = tagToMatch; 
     this.tagContents = tagContents; 
     this.strict = strict; 
    } 

    // These are the temporary values we store as we parse the file 
    private String currentElement; 
    private StringBuilder contents = null; // if not null we are in Movie tag 
    private String currentData; 
    List<String> result = new ArrayList<String>(); // store resulting nodes here 
    private boolean skip = false; 

... 

Эти методы являются реализация ContentHandler. Первый обнаруживает, что элемент найден (начальный тег). Мы сохраняем имя тега (дочерний Movie) в переменной, так как это может быть один мы используем в поиске:

... 

    @Override 
    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { 

     // Store the current element that started now 
     currentElement = qName; 

     // If this is a Movie tag, save the contents because we might need it 
     if (qName.equals("Movie")) { 
      contents = new StringBuilder(); 
     } 

    } 
...  

Это один вызывается каждый раз, когда блок символов называется. Мы проверяем, встречаются ли эти символы внутри интересующего нас элемента. Если это так, мы сопоставляем содержимое и сохраняем его, если оно соответствует.

... 
    @Override 
    public void characters(char[] ch, int start, int length) throws SAXException { 

     // if we discovered that we don't need this data, we skip it 
     if (skip || currentElement == null) { 
      return; 
     } 

     // If we are inside the tag we want to search, save the contents 
     currentData = new String(ch, start, length); 

     if (currentElement.equals(tagToMatch)) { 
      boolean discard = true; 

      if (strict) { 
       if (currentData.equals(tagContents)) { // exact match 
        discard = false; 
       } 

      } else { 
       if (currentData.toLowerCase().indexOf(tagContents.toLowerCase()) >= 0) { // matches occurrence of substring 
        discard = false; 
       } 
      } 

      if (discard) { 
       skip = true; 
      } 
     } 

    } 
...  

Это называется, когда найден конечный тег. Мы можем теперь добавить его к документу, который мы строим в памяти, если хотим.

... 
    @Override 
    public void endElement(String uri, String localName, String qName) throws SAXException { 

     // Rebuild the XML if it's a node we didn't skip 
     if (qName.equals("Movie")) { 
      if (!skip) { 
       result.add(contents.insert(0, "<Movie>").append("</Movie>").toString()); 
      } 

      // reset the variables so we can check the next node 
      contents = null; 
      skip = false; 
     } else if (contents != null && !skip) { 
      contents.append("<").append(qName).append(">") 
        .append(currentData) 
        .append("</").append(qName).append(">"); 
     } 

     currentElement = null; 
    } 
...  

И наконец, этот вызов вызывается, когда документ заканчивается. Я также использовал его для печати результата в конце.

... 
    @Override 
    public void endDocument() throws SAXException { 
     StringBuilder resultFile = new StringBuilder(); 
     resultFile.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 
     resultFile.append("<Movies>"); 
     for (String childNode : result) { 
      resultFile.append(childNode.toString()); 
     } 
     resultFile.append("</Movies>"); 

     System.out.println("=== Resulting XML containing Movies where " + tagToMatch + " is one of " + tagContents + " ==="); 
     System.out.println(resultFile.toString()); 
    } 

} 

Здесь представлено небольшое приложение Java, которое загружает этот файл и использует обработчик событий для извлечения данных.

public class SAXReaderExample { 

    public static final String PATH = "src/main/resources"; // this is where I put the XML file 

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { 

     // Obtain XML Reader 
     SAXParserFactory spf = SAXParserFactory.newInstance(); 
     SAXParser sp = spf.newSAXParser(); 
     XMLReader reader = sp.getXMLReader(); 

     // Instantiate SAX handler 
     ExtractMovieSaxHandler handler = new ExtractMovieSaxHandler(); 

     // set search criteria 
     handler.setSearchCriteria("Director", "Kubrick", false); 

     // Register handler with XML reader 
     reader.setContentHandler(handler); 

     // Parse the XML 
     reader.parse(new InputSource(new FileInputStream(new File(PATH, "source.xml")))); 
    } 
} 

Вот результирующий файл, после обработки:

<?xml version="1.0" encoding="UTF-8"?> 
<Movies> 
    <Movie> 
     <Imdb>tt0062622</Imdb> 
     <Title>2001: A Space Odyssey</Title> 
     <Director>Stanley Kubrick</Director> 
     <Year>1968</Year> 
     <Duration>160</Duration> 
    </Movie> 
    <Movie> 
     <Imdb>tt0066921</Imdb> 
     <Title>A Clockwork Orange</Title> 
     <Director>Stanley Kubrick</Director> 
     <Year>1972</Year> 
     <Duration>136</Duration> 
    </Movie> 
    <Movie> 
     <Imdb>tt0081505</Imdb> 
     <Title>The Shining</Title> 
     <Director>Stanley Kubrick</Director> 
     <Year>1980</Year> 
     <Duration>144</Duration> 
    </Movie> 
    ... 
</Movies> 

Ваш сценарий может быть различным, но этот пример показывает общее решение, которое вы, вероятно, может адаптироваться к вашей проблеме. Более подробную информацию вы можете найти в руководствах по SAX и JAXP.

1

500Mb хорошо в пределах того, что может быть достигнуто с помощью XSLT. Это немного зависит от того, сколько усилий вы хотите потратить, чтобы разработать оптимальное решение: т. Е. Более дорогое, ваше время или время машины?

+0

хорошо, очевидно, время машины более экспансивное, так как оно будет работать в соответствии с моим решением уже после того, как я закончу его разработку :) Хотя, мой вопрос касался не ограничений XSLT, а DOM в контексте от размера ... – theexplorer

+0

Я не понимаю, почему вы хотели бы использовать DOM. Если вы используете XSLT-процессор, он построит дерево в памяти, но большинство процессоров XSLT имеют внутреннее древовидное представление, которое более экономично, чем DOM. –

+0

Я просто хочу знать предел DOM, я не сказал, что хочу его использовать ... Я заранее не знал о XSLT, но сейчас изучаю это. Мой вопрос остается - может ли кто-нибудь предоставить информацию об ограничении в контексте размера файла парсера DOM? (в образовательных целях) спасибо! – theexplorer

Смежные вопросы