Ft/bj-139/xml (#27)

* BJ-139: Added xml handler

* Added object types, constants moved to other class

* fix constant's modifiers

* Added handler's reuseable, support null-values

* Added xml processor

* Moved constants to other class

* Added some exception handling

* fix

* Added additional description in exception handling

* Fixed after comments

* Removed main method, fixed doc

* Removed error handling in method writeEndElement

* Added throwing runtime exception when init

* Added snapshot-version in poms

* Fixed filter parent pom version
This commit is contained in:
Inal Arsanukaev 2017-03-21 15:06:52 +03:00 committed by GitHub
parent 502e9f32a2
commit d327db4ab2
13 changed files with 426 additions and 20 deletions

View File

@ -3,7 +3,7 @@
![default](https://cloud.githubusercontent.com/assets/5084395/23034038/cf7e5eb0-f493-11e6-8698-66262306ca81.png)
### В комплекте ```нужное зачеркнуть```:
- serializer (msgpack, jolt, thrift, ~~json~~)
- serializer (msgpack, jolt, thrift, xml, ~~json~~)
- mock
- ~~migrator~~
- ~~filter~~
@ -14,4 +14,4 @@
Собрать и инсталировать jar(s) в локальный мавен репозиторий(без локально установленного трифта):
* make wc_java_install LOCAL_BUILD=true SETTINGS_XML=path_to_rbk_maven_settings
* make wc_java_install LOCAL_BUILD=true SETTINGS_XML=path_to_rbk_maven_settings

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.rbkmoney.geck</groupId>
<artifactId>parent</artifactId>
<version>0.1.0</version>
<version>0.2.0-SNAPSHOT</version>
</parent>
<artifactId>common</artifactId>
@ -18,5 +18,5 @@
<artifactId>libthrift</artifactId>
</dependency>
</dependencies>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.rbkmoney.geck</groupId>
<artifactId>parent</artifactId>
<version>0.1-SNAPSHOT</version>
<version>0.2.0-SNAPSHOT</version>
</parent>
<artifactId>filter</artifactId>
@ -37,4 +37,4 @@
</plugins>
</build>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.rbkmoney.geck</groupId>
<artifactId>parent</artifactId>
<version>0.1.0</version>
<version>0.2.0-SNAPSHOT</version>
</parent>
<artifactId>migrator</artifactId>
@ -25,4 +25,4 @@
</dependencies>
</project>
</project>

View File

@ -12,7 +12,7 @@
<groupId>com.rbkmoney.geck</groupId>
<artifactId>parent</artifactId>
<version>0.1.0</version>
<version>0.2.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
@ -126,4 +126,4 @@
</pluginManagement>
</build>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.rbkmoney.geck</groupId>
<artifactId>parent</artifactId>
<version>0.1.0</version>
<version>0.2.0-SNAPSHOT</version>
</parent>
<artifactId>serializer</artifactId>

View File

@ -11,7 +11,13 @@ public enum StructType {
SET("set"),
LIST("list"),
STRUCT("struct"),
BOOL("bool"),
STRING("string"),
DOUBLE("double"),
LONG("long"),
NULL("null"),
BYTEARRAY("bytearray"),
MAP_ENTRY("map_entry"),
OTHER("");
private static class Holder {
static Map<String, StructType> MAP = new HashMap<>();

View File

@ -0,0 +1,13 @@
package com.rbkmoney.geck.serializer.kit.xml;
/**
* Created by inalarsanukaev on 16.03.17.
*/
public interface XMLConstants {
String KEY = "key";
String VALUE = "value";
String ROOT = "root";
String ELEMENT = "elem";
String ATTRIBUTE_TYPE = "type";
String ATTRIBUTE_SIZE = "size";
}

View File

@ -0,0 +1,240 @@
package com.rbkmoney.geck.serializer.kit.xml;
import com.rbkmoney.geck.common.stack.ByteStack;
import com.rbkmoney.geck.serializer.StructHandler;
import com.rbkmoney.geck.serializer.exception.BadFormatException;
import com.rbkmoney.geck.serializer.kit.EventFlags;
import com.rbkmoney.geck.serializer.kit.StructType;
import static com.rbkmoney.geck.serializer.kit.xml.XMLConstants.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMResult;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Base64;
/**
* Created by inalarsanukaev on 14.03.17.
*/
public class XMLHandler implements StructHandler<DOMResult> {
private ByteStack stack = new ByteStack();
private DOMResult result;
private XMLStreamWriter out;
private DocumentBuilder documentBuilder;
private XMLOutputFactory xmlOutputFactory;
{
try {
init();
} catch (BadFormatException e) {
throw new RuntimeException(e);//TODO
}
}
private void init() throws BadFormatException {
try {
getDocumentBuilder().reset();
result = new DOMResult(getDocumentBuilder().newDocument());
out = getXmlOutputFactory().createXMLStreamWriter(result);
out.writeStartDocument();
out.writeStartElement(ROOT);
} catch (XMLStreamException e) {
throw new BadFormatException("Unknown error when init", e);
}
}
private XMLOutputFactory getXmlOutputFactory() {
if (xmlOutputFactory == null) {
xmlOutputFactory = XMLOutputFactory.newInstance();
}
return xmlOutputFactory;
}
private DocumentBuilder getDocumentBuilder() throws BadFormatException {
if (documentBuilder == null) {
try {
documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new BadFormatException("Unknown error when getDocumentBuilder", e);
}
}
return documentBuilder;
}
private void writeStartElement(String type, int size) throws BadFormatException {
try {
if (!stack.isEmpty()) {
byte x = stack.peek();
if (x == EventFlags.startList || x == EventFlags.startSet) {
out.writeStartElement(ELEMENT);
}
}
if (type != null) {
out.writeAttribute(ATTRIBUTE_TYPE, type);
}
if (size >= 0) {
out.writeAttribute(ATTRIBUTE_SIZE, String.valueOf(size));
}
} catch (XMLStreamException e) {
throw new BadFormatException("Unknown error when writeStartElement", e);
}
}
private void writeStartElement(String type) throws BadFormatException {
writeStartElement(type, -1);
}
private void writeValue(String value, String type) throws BadFormatException {
try {
writeStartElement(type);
if (value != null) {
out.writeCharacters(value.toString());
}
out.writeEndElement();
} catch (XMLStreamException e) {
throw new BadFormatException("Unknown error when writeValue", e);
}
}
private void writeEndElement() throws BadFormatException {
try {
out.writeEndElement();
} catch (XMLStreamException e) {
throw new BadFormatException("Unknown error when writeEndElement", e);
}
}
@Override
public void beginStruct(int size) throws IOException {
writeStartElement(StructType.STRUCT.getKey(), size);
stack.push(EventFlags.startStruct);
}
@Override
public void endStruct() throws IOException {
stack.pop();
writeEndElement();
}
@Override
public void beginList(int size) throws IOException {
writeStartElement(StructType.LIST.getKey(), size);
stack.push(EventFlags.startList);
}
@Override
public void endList() throws IOException {
stack.pop();
writeEndElement();
}
@Override
public void beginSet(int size) throws IOException {
writeStartElement(StructType.SET.getKey(), size);
stack.push(EventFlags.startSet);
}
@Override
public void endSet() throws IOException {
stack.pop();
writeEndElement();
}
@Override
public void beginMap(int size) throws IOException {
stack.push(EventFlags.startMap);
try {
out.writeAttribute(ATTRIBUTE_TYPE, StructType.MAP.getKey());
out.writeAttribute(ATTRIBUTE_SIZE, String.valueOf(size));
} catch (XMLStreamException e) {
throw new BadFormatException("Unknown error when beginMap", e);
}
}
@Override
public void endMap() throws IOException {
stack.pop();
writeEndElement();
}
@Override
public void beginKey() throws IOException {
try {
out.writeStartElement(ELEMENT);
out.writeAttribute(ATTRIBUTE_TYPE, StructType.MAP_ENTRY.getKey());
} catch (XMLStreamException e) {
throw new BadFormatException("Unknown error when beginKey", e);
}
name(KEY);
}
@Override
public void endKey() throws IOException {
}
@Override
public void beginValue() throws IOException {
name(VALUE);
}
@Override
public void endValue() throws IOException {
writeEndElement();
}
@Override
public void name(String name) throws IOException {
try {
out.writeStartElement(name);
} catch (XMLStreamException e) {
throw new BadFormatException("Unknown error when name", e);
}
}
@Override
public void value(boolean value) throws IOException {
writeValue(String.valueOf(value), StructType.BOOL.getKey());
}
@Override
public void value(String value) throws IOException {
writeValue(value, StructType.STRING.getKey());
}
@Override
public void value(double value) throws IOException {
writeValue(String.valueOf(value), StructType.DOUBLE.getKey());
}
@Override
public void value(long value) throws IOException {
writeValue(String.valueOf(value), StructType.LONG.getKey());
}
@Override
public void value(byte[] value) throws IOException {
writeValue(Base64.getEncoder().encodeToString(value), StructType.BYTEARRAY.getKey());
}
@Override
public void nullValue() throws IOException {
writeValue(null, StructType.NULL.getKey());
}
@Override
public DOMResult getResult() throws IOException {
try {
out.writeEndDocument();
out.flush();
} catch (XMLStreamException e) {
throw new BadFormatException("Unknown error when getResult", e);
}
DOMResult readyResult = result;
init();
return readyResult;
}
}

View File

@ -0,0 +1,105 @@
package com.rbkmoney.geck.serializer.kit.xml;
import com.rbkmoney.geck.serializer.StructHandler;
import com.rbkmoney.geck.serializer.StructProcessor;
import com.rbkmoney.geck.serializer.exception.BadFormatException;
import com.rbkmoney.geck.serializer.kit.StructType;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.transform.dom.DOMResult;
import java.io.IOException;
import java.sql.Struct;
import java.util.Arrays;
import java.util.Base64;
/**
* Created by inalarsanukaev on 16.03.17.
*/
public class XMLProcessor implements StructProcessor<DOMResult> {
@Override
public <R> R process(DOMResult value, StructHandler<R> handler) throws IOException {
Node document = value.getNode().getFirstChild();
String nodeName = document.getNodeName();
if (!nodeName.equals(XMLConstants.ROOT)) {
throw new BadFormatException("Wrong root element name. Expected '"+XMLConstants.ROOT+"', actual '"+nodeName+"'");
}
processNode((Element) document, handler, false);
return handler.getResult();
}
protected void processNode(Element node, StructHandler handler, boolean printName) throws IOException {
if (node != null){
String nodeName = node.getNodeName();
if (printName) {
handler.name(nodeName);
}
StructType type = StructType.valueOfKey(node.getAttribute(XMLConstants.ATTRIBUTE_TYPE));
if (type == null) {
throw new BadFormatException("Attribute 'type' must not be null. Node name: "+nodeName);
}
switch (type) {
case STRING:
handler.value(node.getTextContent());
break;
case BOOL:
handler.value(Boolean.valueOf(node.getTextContent()));
break;
case BYTEARRAY:
handler.value(Base64.getDecoder().decode(node.getTextContent()));
break;
case DOUBLE:
handler.value(Double.valueOf(node.getTextContent()));
break;
case LONG:
handler.value(Long.valueOf(node.getTextContent()));
break;
case NULL:
handler.nullValue();
break;
case STRUCT:
handler.beginStruct(Integer.parseInt(node.getAttribute(XMLConstants.ATTRIBUTE_SIZE)));
processChildNodes(node, handler, true);
handler.endStruct();
break;
case LIST:
handler.beginList(Integer.parseInt(node.getAttribute(XMLConstants.ATTRIBUTE_SIZE)));
processChildNodes(node, handler, false);
handler.endList();
break;
case SET:
handler.beginSet(Integer.parseInt(node.getAttribute(XMLConstants.ATTRIBUTE_SIZE)));
processChildNodes(node, handler, false);
handler.endSet();
break;
case MAP:
handler.beginMap(Integer.parseInt(node.getAttribute(XMLConstants.ATTRIBUTE_SIZE)));
processChildNodes(node, handler, false);
handler.endMap();
break;
case MAP_ENTRY :
handler.beginKey();
processNode((Element) node.getFirstChild(), handler, false);
handler.endKey();
handler.beginValue();
processNode((Element) node.getLastChild(), handler, false);
handler.endValue();
break;
default:
new BadFormatException("Unknown type of node: "+type+". Must be on of them : "+ Arrays.toString(StructType.values()));
}
}
}
private void processChildNodes(Element node, StructHandler handler, boolean printName) throws IOException {
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); ++i) {
Node item = nodeList.item(i);
if (item.getNodeType() == Node.ELEMENT_NODE) {
processNode((Element) item, handler, printName);
} else {
throw new BadFormatException("Wrong type of node. Expected - "+Node.ELEMENT_NODE+", actual - "+item.getNodeType());
}
}
}
}

View File

@ -16,15 +16,14 @@ import com.rbkmoney.geck.serializer.kit.object.ObjectHandler;
import com.rbkmoney.geck.serializer.kit.object.ObjectProcessor;
import com.rbkmoney.geck.serializer.kit.tbase.TBaseHandler;
import com.rbkmoney.geck.serializer.kit.tbase.TBaseProcessor;
import com.rbkmoney.geck.serializer.kit.xml.XMLHandler;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.*;
/**
* Created by inalarsanukaev on 22.02.17.
@ -38,7 +37,12 @@ public class DamselTest {
System.out.println(json);
new JSONObject(json);
}
@Test
public void xmlInvoiceTest() throws Exception {
com.rbkmoney.damsel.v130.payment_processing.InvoicePaymentStarted invoice = new MockTBaseProcessor(MockMode.ALL, new FixedValueGenerator()).process(new com.rbkmoney.damsel.v130.payment_processing.InvoicePaymentStarted(), new TBaseHandler<>(com.rbkmoney.damsel.v130.payment_processing.InvoicePaymentStarted.class));
String xml = new TBaseProcessor().process(invoice, new XMLHandler()).toString();
System.out.println(xml);
}
@Test
public void testInvoiceMsgPack() throws IOException {
InvoicePaymentStarted invoice = GeckTestUtil.getInvoicePaymentStarted();
@ -60,7 +64,6 @@ public class DamselTest {
new TBaseHandler<>(InvoicePaymentStarted.class));
Assert.assertEquals(invoice1, invoice2);
}
@Test
public void testInvoiceBackTransform2() throws IOException {
InvoicePaymentStarted invoice1 = GeckTestUtil.getInvoicePaymentStarted();

View File

@ -0,0 +1,38 @@
package com.rbkmoney.geck.serializer.kit.xml;
import com.rbkmoney.damsel.v130.payment_processing.InvoicePaymentStarted;
import com.rbkmoney.geck.serializer.GeckTestUtil;
import com.rbkmoney.geck.serializer.kit.mock.FixedValueGenerator;
import com.rbkmoney.geck.serializer.kit.mock.MockMode;
import com.rbkmoney.geck.serializer.kit.mock.MockTBaseProcessor;
import com.rbkmoney.geck.serializer.kit.tbase.TBaseHandler;
import com.rbkmoney.geck.serializer.kit.tbase.TBaseProcessor;
import com.rbkmoney.geck.serializer.test.TestObject;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
/**
* Created by inalarsanukaev on 17.03.17.
*/
public class XMLTest {
@Test
public void testInvoiceBackTransform1() throws IOException {
InvoicePaymentStarted invoice1 = GeckTestUtil.getInvoicePaymentStarted();
InvoicePaymentStarted invoice2 =
new XMLProcessor().process(
new TBaseProcessor().process(invoice1, new XMLHandler()),
new TBaseHandler<>(InvoicePaymentStarted.class));
Assert.assertEquals(invoice1, invoice2);
}
@Test
public void xmlKebabTest() throws Exception {
TestObject invoice = new MockTBaseProcessor(MockMode.ALL, new FixedValueGenerator()).process(new TestObject(), new TBaseHandler<>(TestObject.class));
XMLHandler handler = new XMLHandler();
String xml = new TBaseProcessor().process(invoice, handler).toString();
//test re-use handler
new TBaseProcessor().process(invoice, handler);
System.out.println(xml);
}
}

View File

@ -10,9 +10,10 @@
},
"*" : "payment.&"
},
"*": {
"@" : "&"
}
"cash_flow": {
"@" : "cash_flow.final_cash_flow"
},
"*": "&"
}
},
{