package com.fasterxml.jackson.dataformat.xml.stream;
import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
public class XmlParserTest extends XmlTestBase
{
protected JsonFactory _jsonFactory;
protected XmlFactory _xmlFactory;
protected XmlMapper _xmlMapper;
// let's actually reuse XmlMapper to make things bit faster
@Override
public void setUp() throws Exception {
super.setUp();
_jsonFactory = new JsonFactory();
_xmlFactory = new XmlFactory();
_xmlMapper = new XmlMapper();
}
/*
/**********************************************************
/* Unit tests
/**********************************************************
*/
public void testSimplest() throws Exception
{
assertEquals("{\"leaf\":\"abc\"}",
_readXmlWriteJson("<root><leaf>abc</leaf></root>"));
}
public void testSimpleWithEmpty() throws Exception
{
// 06-Jan-2015, tatu: Not superbly simple, actually; whether we'll have `null`
// or `{}` depends on context; if in array context, latter; otherwise former
assertEquals("{\"leaf\":null}",
_readXmlWriteJson("<root><leaf /></root>"));
}
public void testSimpleNested() throws Exception
{
assertEquals("{\"a\":{\"b\":{\"c\":\"xyz\"}}}",
_readXmlWriteJson("<root><a><b><c>xyz</c></b></a></root>"));
}
/**
* Unit test that verifies that we can write sample document from JSON
* specification as XML, and read it back in "as JSON", with
* expected transformation.
*/
public void testRoundTripWithSample() throws Exception
{
// First: let's convert from sample JSON doc to default xml output
JsonNode root = new ObjectMapper().readTree(SAMPLE_DOC_JSON_SPEC);
String xml = _xmlMapper.writeValueAsString(root);
// Here we would ideally use base class test method. Alas, it won't
// work due to couple of problems;
// (a) All values are reported as Strings (not ints, for example
// (b) XML mangles arrays, so all we see are objects.
// Former could be worked around; latter less so at this point.
// So, for now, let's just do sort of minimal verification, manually
JsonParser p = _xmlMapper.getFactory().createParser(xml);
assertToken(JsonToken.START_OBJECT, p.nextToken()); // main object
assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Image'
verifyFieldName(p, "Image");
assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'image' object
assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width'
verifyFieldName(p, "Width");
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(String.valueOf(SAMPLE_SPEC_VALUE_WIDTH), p.getText());
assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height'
verifyFieldName(p, "Height");
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(String.valueOf(SAMPLE_SPEC_VALUE_HEIGHT), p.getText());
assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Title'
verifyFieldName(p, "Title");
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(p));
assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Thumbnail'
verifyFieldName(p, "Thumbnail");
assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'thumbnail' object
assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Url'
verifyFieldName(p, "Url");
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(p));
assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height'
verifyFieldName(p, "Height");
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(String.valueOf(SAMPLE_SPEC_VALUE_TN_HEIGHT), p.getText());
assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width'
verifyFieldName(p, "Width");
// Width value is actually a String in the example
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(p));
assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'thumbnail' object
// Note: arrays are "eaten"; wrapping is done using BeanPropertyWriter, so:
//assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'IDs'
//verifyFieldName(p, "IDs");
//assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'ids' array
assertToken(JsonToken.FIELD_NAME, p.nextToken());
verifyFieldName(p, "IDs");
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(String.valueOf(SAMPLE_SPEC_VALUE_TN_ID1), getAndVerifyText(p));
assertToken(JsonToken.FIELD_NAME, p.nextToken());
verifyFieldName(p, "IDs");
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(String.valueOf(SAMPLE_SPEC_VALUE_TN_ID2), getAndVerifyText(p));
assertToken(JsonToken.FIELD_NAME, p.nextToken());
verifyFieldName(p, "IDs");
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(String.valueOf(SAMPLE_SPEC_VALUE_TN_ID3), getAndVerifyText(p));
assertToken(JsonToken.FIELD_NAME, p.nextToken());
verifyFieldName(p, "IDs");
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(String.valueOf(SAMPLE_SPEC_VALUE_TN_ID4), getAndVerifyText(p));
// no matching entry for array:
//assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'ids' array
assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'image' object
assertToken(JsonToken.END_OBJECT, p.nextToken()); // main object
p.close();
}
/**
* Test to ensure functionality used to force an element to be reported
* as "JSON" Array, instead of default Object.
*/
public void testForceElementAsArray() throws Exception
{
final String XML = "<array><elem>value</elem><elem><property>123</property></elem><elem>1</elem></array>";
FromXmlParser xp = (FromXmlParser) _xmlFactory.createParser(new StringReader(XML));
// First: verify handling without forcing array handling:
assertToken(JsonToken.START_OBJECT, xp.nextToken()); // <array>
assertToken(JsonToken.FIELD_NAME, xp.nextToken()); // <elem>
assertEquals("elem", xp.getCurrentName());
assertToken(JsonToken.VALUE_STRING, xp.nextToken());
assertEquals("value", xp.getText());
assertToken(JsonToken.FIELD_NAME, xp.nextToken()); // <elem>
assertEquals("elem", xp.getCurrentName());
assertToken(JsonToken.START_OBJECT, xp.nextToken()); // <property>
assertToken(JsonToken.FIELD_NAME, xp.nextToken());
assertEquals("property", xp.getCurrentName());
assertToken(JsonToken.VALUE_STRING, xp.nextToken());
assertEquals("123", xp.getText());
assertToken(JsonToken.END_OBJECT, xp.nextToken()); // <object>
assertToken(JsonToken.FIELD_NAME, xp.nextToken()); // <elem>
assertEquals("elem", xp.getCurrentName());
assertToken(JsonToken.VALUE_STRING, xp.nextToken());
assertEquals("1", xp.getText());
assertToken(JsonToken.END_OBJECT, xp.nextToken()); // </array>
xp.close();
// And then with array handling:
xp = (FromXmlParser) _xmlFactory.createParser(new StringReader(XML));
assertTrue(xp.getParsingContext().inRoot());
assertToken(JsonToken.START_OBJECT, xp.nextToken()); // <array>
assertTrue(xp.getParsingContext().inObject()); // true until we do following:
// must request 'as-array' handling, which will "convert" current token:
assertTrue("Should 'convert' START_OBJECT to START_ARRAY", xp.isExpectedStartArrayToken());
assertToken(JsonToken.START_ARRAY, xp.getCurrentToken()); // <elem>
assertTrue(xp.getParsingContext().inArray());
assertToken(JsonToken.VALUE_STRING, xp.nextToken());
assertTrue(xp.getParsingContext().inArray());
assertEquals("value", xp.getText());
assertToken(JsonToken.START_OBJECT, xp.nextToken()); // <property>
assertTrue(xp.getParsingContext().inObject());
assertToken(JsonToken.FIELD_NAME, xp.nextToken());
assertEquals("property", xp.getCurrentName());
assertToken(JsonToken.VALUE_STRING, xp.nextToken());
assertEquals("123", xp.getText());
StringWriter w = new StringWriter();
assertEquals(3, xp.getText(w));
assertEquals("123", w.toString());
assertTrue(xp.getParsingContext().inObject());
assertToken(JsonToken.END_OBJECT, xp.nextToken()); // </property>
assertTrue(xp.getParsingContext().inArray());
assertToken(JsonToken.VALUE_STRING, xp.nextToken());
assertTrue(xp.getParsingContext().inArray());
assertEquals("1", xp.getText());
assertToken(JsonToken.END_ARRAY, xp.nextToken()); // </array>
assertTrue(xp.getParsingContext().inRoot());
xp.close();
}
public void testXmlAttributes() throws Exception
{
final String XML = "<data max=\"7\" offset=\"9\"/>";
FromXmlParser xp = (FromXmlParser) _xmlFactory.createParser(new StringReader(XML));
// First: verify handling without forcing array handling:
assertToken(JsonToken.START_OBJECT, xp.nextToken()); // <data>
assertToken(JsonToken.FIELD_NAME, xp.nextToken()); // <max>
assertEquals("max", xp.getCurrentName());
assertToken(JsonToken.VALUE_STRING, xp.nextToken());
assertEquals("7", xp.getText());
assertToken(JsonToken.FIELD_NAME, xp.nextToken()); // <offset>
assertEquals("offset", xp.getCurrentName());
StringWriter w = new StringWriter();
assertEquals(6, xp.getText(w));
assertEquals("offset", w.toString());
assertToken(JsonToken.VALUE_STRING, xp.nextToken());
assertEquals("9", xp.getText());
w = new StringWriter();
assertEquals(1, xp.getText(w));
assertEquals("9", w.toString());
assertToken(JsonToken.END_OBJECT, xp.nextToken()); // </data>
xp.close();
}
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
private String _readXmlWriteJson(String xml) throws IOException
{
StringWriter w = new StringWriter();
JsonParser p = _xmlFactory.createParser(xml);
JsonGenerator jg = _jsonFactory.createGenerator(w);
while (p.nextToken() != null) {
jg.copyCurrentEvent(p);
}
p.close();
jg.close();
return w.toString();
}
}