Я делаю некоторые хирургические преобразования XML с использованием XMLEventReader
и XMLEventWriter
. По большей части я просто пишу эти события, когда они читаются:
import javax.xml.stream.*;
import javax.xml.stream.events.XMLEvent;
import java.io.StringReader;
import java.io.StringWriter;
public class StaxExample {
public static void main(String[] args) throws XMLStreamException {
String inputXml =
"<foo>" +
" <bar baz=\"a b c \"/>" +
" <changeme/>" +
"</foo>";
StringWriter result = new StringWriter();
XMLEventReader reader = XMLInputFactory.newFactory().createXMLEventReader(new StringReader(inputXml));
XMLEventWriter writer = XMLOutputFactory.newFactory().createXMLEventWriter(result);
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
//in real code, look for "changeme" and insert some stuff
writer.add(event);
}
System.out.println(result.toString());
}
}
Моя проблема заключается в следующем:
<?xml version="1.0" ?><foo> <bar baz="a
b
c
"></bar> <changeme></changeme></foo>
Хотя синтаксически корректный XML, необходимо (из-за потребителя ниже по течению), что я сохраняю новые строки. Вышеупомянутый XML вместо этого будет нормализован для abc
этим потребителем (и даже самим StAX), если я возьму этот вывод и верну его обратно в ту же программу, во второй раз он выведет baz="abc "
).
Хотя я отказался от XMLEventWriter
сохраняющего не-семантическое форматирование, существует ли способ предотвратить существенное изменение моих значений атрибутов?
Если вам нужна абсолютная точность о том, где избежать новых строк в XML, а где нет (т.е. Вам нужно избегать новых строк только внутри атрибутов, а не в другом месте), у меня есть другое предложение, сложное немного сложнее:
Посмотрите на свой код:
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
//in real code, look for "changeme" and insert some stuff
writer.add(event);
}
Существует один момент, когда вы можете вставить между атрибутом и писателем: сразу после инициализации event
и перед его передачей в writer.add
вы можете инкапсулировать событие в свою собственную реализацию XMLEvent
чтобы убедиться, что если это экземпляр javax.xml.stream.events.Attribute
, вы будете перезаписывать Attribute.getValue()
чтобы вернуть значение, соответствующее escapped.
Но есть дополнительное усложнение: XMLEvents, возвращаемые XMLEventReader, обычно не включают события атрибутов: Атрибуты включены в соответствующие события StartElement. Таким образом, вам нужен еще один уровень инкапсуляции: объекты StartElement, а затем содержащиеся объекты Attribute.
Ну, я предлагаю вам реализовать свой собственный Writer:
public class EscappingNLWriter extends FilterWriter
{
public EscappingNLWriter(Writer out) {super(out);}
public void write(c)
{
if (c=='\n')
{
out.write(" ");
}
else
{
out.write(c);
}
}
public void write(char[] buff, int offset, int len) throws IOException
{
// ...Same char filtering...
}
public void write(String str, int offset, int len) throws IOException
{
// ...Same char filtering...
}
}
И затем используйте его для инкапсуляции StringWriter:
Writer result = new EscappingNLWriter(new StringWriter());