Вопрос по sax, xml, filter, java – Вставьте новый элемент в файл XML с помощью SAX Filter

4

У меня есть файл XMl, который выглядит так:

<?xml version="1.0" encoding="UTF-8"?>
<game >
  <moves>
    <turn>2</turn>
    <piece nr="1" />
    <turn>4</turn>
    <piece nr="1" />

  </moves>
</game>

Я пишу программу на Java, которая принимает XML-файл в качестве входных данных, затем анализирует его с помощью фильтров SAX и SAX и вычисляет:

the sum of the content of turn element (here=6) the number of piece elements (here=2)

Затем я хочу использовать SAX-фильтр для генерации выходного XML-файла, который совпадает с входным, но с дополнительным элементом, таким как:

<s:statistics>
    <s:turn-total>6</s:turn-total>
    <s:piece-count>2</s:piece-count>
</s:statistics>

Префиксs это ссылка наПространство имен.

Моя программа до сих пор:

 public class test{     
         public static void main(String[] args) throws Exception {
                if (args.length != 2) {
                System.err.println("error ");
                System.exit(1);
                }
                String xmlInput = args[0];
                String filteredXML = args[1];
                test test1 = new test();
                    test1.sax(xmlInput, filteredXML);
            }
    private void sax(String gameXML, String filteredGameXML)throws Exception{
        FileInputStream fis = new FileInputStream( gameXML);
        InputSource is = new InputSource(fis);
        XMLReader xr = XMLReaderFactory.createXMLReader();
        XMLFilter xf = new MyFilter();
        xf.setParent(xr);
        xr = xf;
        xr.parse(is);
        xr.setFeature("http://xml.org/sax/features/namespaces", true);
        DefaultHandler handler = new DefaultHandler();
        xr.setContentHandler(handler);
    }
    private class MyFilter extends XMLFilterImpl{
             StringBuffer buffer;
         int temp=0;
             int sum=0;
             String ff;
             int numof=0;
             private MyFilter() {}

            @Override
                public void startDocument() throws SAXException {
                     System.out.println( "START DOCUMENT" );
                    numof=0;        
                }

                public void startElement(String namespaceURI, String localName, String  name,   Attributes attributes) throws SAXException{
                    if(localName.equals("turn")){
                        buffer=new StringBuffer();
                        }
                    if("piece".equals(name)){
                        numof++;
                    }
                }

                public void characters(char[] ch, int start, int length) throws SAXException {
                    String s=new String(ch, start, length);
                    if(buffer!=null){
                        buffer.append(s);
                        }
                }

                public void endElement(String uri, String localName, String name)throws SAXException {
                    if(buffer!=null ){
                        ff=buffer.toString();
                        temp=Integer.valueOf(ff);
                        sum=sum+temp;
                        }
                        buffer=null;
                }
                public void endDocument() throws SAXException {
                    System.out.println( "END DOCUMENT" );
                    System.out.println("sum of turn: "+ sum);
                    System.out.println("sum of piece: "+ numof);
                }
         }

    }

Что я должен делать дальше?

@ сорок два: На самом деле это массив символов, так что проблем с кодировкой не возникнет, но вы правы в том, что новая строка не нужна. StringBuffer имеет метод для непосредственного добавления частей массива char. Jörn Horstmann
я хочу знать, как я могу вставить новый элемент & lt; s: statistics & gt; & quot; в XML-файл? и как я могу сгенерировать XML-файл в качестве вывода? Kenan Hadad
С SAX Filter вы можете изменить проанализированное дерево, но вы не можете вывести измененный XML-файл. Для лучшего понимания, пожалуйста, обратитесь к этому:cafeconleche.org/books/xmljava/chapters/ch08.html Вероятно, лучший способ - это использовать DocumentBuilder -docs.oracle.com/javase/1.5.0/docs/api/javax/xml/parsers/… или SAXBuilder -jdom.org/docs/apidocs/org/jdom2/input/SAXBuilder.html добавить элемент после того, как вы его создали. Надеюсь, это помогло Mircea
И в чем проблема? Вы сказали нам, чего хотите достичь, и дали нам дамп кода. Код не работает? Если нет, то какие ошибки вы получаете. Если это работает, но не так, как ожидалось, как результат отличается от ваших ожиданий? Пожалуйста, уточните свой вопрос. Chris
Существует проблема с тем, как вы преобразуете символы в строку - это, вероятно, не укусит вас этим вводом, но просто для понимания: анализатор может свободно передавать вам содержимое символа в произвольных кусках. Поскольку в содержимом могут быть неиспользуемые символы ascii, т. Е. Более одного байта, вы не можете преобразовать массив байтов в строку, пока не получите его полностью, в методе enedElement (). forty-two

Ваш Ответ

3   ответа
4

XMLFilter должен делегировать другомуContentHandler который сериализует документ на основе событий саксофона.

SAXTransformerFactory factory = (SAXTransformerFactory)TransformerFactory.newInstance();
TransformerHandler serializer = factory.newTransformerHandler();
Result result = new StreamResult(...);
serializer.setResult(result);

XMLFilterImpl filter = new MyFilter();
filter.setContentHandler(serializer);

XMLReader xmlreader = XMLReaderFactory.createXMLReader();
xmlreader.setFeature("http://xml.org/sax/features/namespaces", true);
xmlreader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
xmlreader.setContentHandler(filter);

xmlreader.parse(new InputSource(...));

обратный вызов должен делегироватьsuper реализация, которая направляет события в сериализациюContentHandler.

public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
    super.startElement(namespaceURI, localName, qName, atts);
    ...
}

В вашемendElement Вы можете проверить, есть ли у вас последний закрывающий тэг, и добавить дополнительные саксофонные события.

public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
    super.endElement(namespaceURI, localName, qName);
    if ("game".equals(localName)) {
        super.startElement("", "statistics", "statistics", new AttributesImpl());
        char[] chars = String.valueOf(num).toCharArray();
        super.characters(chars, 0, chars.length);
        super.endElement("", "statistics", "statistics");
    }
    ...
}
+1: это правильная тактика. ОП хотел пространство имен для нового элемента, но его было бы относительно легко добавить.
0

//stackoverflow.com/users/139595/jorn-horstmann) сверху, вы можете легко добавить недостающие элементы. Но чтобы записать результаты в файл XML, вы должны использовать TransformerHandler.

Просто создайте очень простой ContentHandler и используйте его вместо DefaultHandler. В ContentHandler вы можете реализовать все основные функции (startDocument, startElement и т. Д.) И добавить каждый элемент в новый Transformer. например В вашей функции startDocument:

Transformer t = hd.getTransformer();
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
t.setOutputProperty(OutputKeys.METHOD, "xml");
t.setOutputProperty(OutputKeys.INDENT, "yes");
hd.startDocument();

И затем (в каждой другой функции) добавить это: например для началаЭлемент:

hd.startElement(uri,localName,name,attributes);

Наконец, вы можете записать все это в файл в методе endDocument.

0

если я ошибаюсь, но я думаю, что XMLReader и XMLFilter не должны изменять документ. Я могу предложить другой подход, который также может изменить содержание вашего документа:

public class ExtXMLConfig {
private JAXBContext context;
private Marshaller m;
private Unmarshaller um;
private Schema schema = null;

/**
 * Creates an ExtXMLConfig-object, which uses rootClass as object to parse
 * and save XML-files.
 * 
 * @param rootClass
 *            the class use create/parse xml-files from
 * @throws JAXBException
 */
public ExtXMLConfig(Class<?> rootClass) throws JAXBException {
    context = JAXBContext.newInstance(rootClass);

    init();
}

/**
 * Creates an ExtXMLConfig, which uses a classPath like javax.xml.bin to use
 * all classes in that path to parse and write xml-files
 * 
 * @param classPath
 *            the class path containing all needed java-objects
 * @throws JAXBException
 */
public ExtXMLConfig(String classPath) throws JAXBException {
    context = JAXBContext.newInstance(classPath);

    init();
}

/**
 * Parses a xml-file into a JavaObject.
 * 
 * @param file
 *            path to the xml-file
 * @return a java-Object
 */
public Object load(String file) {
    return load(new File(file));
}

/**
 * Parses a xml-file into a JavaObject.
 * 
 * @param xml
 *            File-object representing the xml-file
 * @return a java-Object
 */
public Object load(File xml) {
    um.setSchema(schema);

    if (xml.exists() && xml.isFile()) {
        try {
            return um.unmarshal(xml);
        } catch (JAXBException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    } else {
        System.out.println("Failed to open file: " + xml.getAbsolutePath());
    }

    return null;
}

/**
 * Saves a object into a xml-file.
 * 
 * @param xml
 *            the object to save
 * @param file
 *            path to the file to save to
 */
public void save(Object xml, String file) {
    save(xml, new File(file));
}

/**
 * Saves a object into a xml-file.
 * 
 * @param xml
 *            the object to save
 * @param file
 *            File-object representing the file to save to
 */
public void save(Object xml, File file) {
    if (xml != null) {
        m.setSchema(schema);

        if (!file.isDirectory()) {
            try {
                if (!file.exists()) {
                    file.createNewFile();
                }

                m.marshal(xml, file);
            } catch (JAXBException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

/**
 * Returns a formatted string representation of a xml-file given as a
 * java-Object.
 * 
 * @param xml
 *            the java-object to parse the xml from.
 * @return a formatted string representation of the given object
 */
public String toString(Object xml) {
    StringWriter out = new StringWriter();
    try {
        m.setSchema(schema);
        m.marshal(xml, out);

        return out.toString();
    } catch (JAXBException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return null;
}

private void init() throws JAXBException {
    m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");

    um = context.createUnmarshaller();
}

Используя этот класс для анализа ваших xml-файлов, вам понадобится только такой класс:

@XmlRootElement // used to parse this class as xml-Root
public class Game {
   private Move moves;

   public Game() {};

   public void setMove(Move moves) {
      this.moves = moves;
   }

   public Moves getMoves() {
      return this.moves;
   }
}

с Move является экземпляром другого класса, который имеет необходимые поля, а также имеет аннотацию для XmlRootElement.

Надеюсь, это поможет.

спасибо за повтор, он работает, но, к сожалению, я должен сделать это, используя Sax Filters (используя Sax Fliter вы можете изменить ваш XML-файл) Kenan Hadad

Похожие вопросы