Я написал java-конвертер (используя библиотеку Джексона), который превращает несколько объектов JSON в файле в правильный массив JSON:
import java.io.File;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MappingJsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class ParseJson {
ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
File file = new File(args[0]);
JsonNode jn = new Parser().parse(file);
System.out.println(jn.toString());
}
private enum ParserState {
start,
object,
array,
field,
done
};
private static class Parser {
public Parser() {
}
public JsonNode parse(File file) throws Exception {
JsonNodeFactory factory = JsonNodeFactory.instance;
JsonFactory mappingFactory = new MappingJsonFactory();
@SuppressWarnings("deprecation")
JsonParser jp = mappingFactory.createJsonParser(file);
int n = 0;
JsonNode result = null;
JsonNode jn;
while((jn = parseNode(jp, false)) != null) {
if(n == 0) {
result = jn;
} else if(n == 1) {
ArrayNode an = factory.arrayNode();
an.add(result);
an.add(jn);
result = an;
} else if(n > 1) {
ArrayNode an = (ArrayNode)result;
an.add(jn);
} else {
throw new Exception("Unexpected parser state");
}
n++;
}
return result;
}
private JsonNode parseNode(JsonParser jp, boolean current) throws Exception {
JsonNodeFactory factory = JsonNodeFactory.instance;
ParserState state = ParserState.start;
JsonNode result = null;
String fieldName = null;
JsonToken token = current ? jp.getCurrentToken() : jp.nextToken();
for(; token != null; token = jp.nextToken()) {
// System.out.println("Token: "+token+": "+jp.getValueAsString());
switch(token) {
/**
* NOT_AVAILABLE can be returned if {@link JsonParser}
* implementation can not currently return the requested
* token (usually next one), or even if any will be
* available, but that may be able to determine this in
* future. This is the case with non-blocking parsers --
* they can not block to wait for more data to parse and
* must return something.
*/
case NOT_AVAILABLE: {
break;
}
/**
* START_OBJECT is returned when encountering '{'
* which signals starting of an Object value.
*/
case START_OBJECT: {
switch(state) {
case start: {
assert result == null;
assert fieldName == null;
result = factory.objectNode();
state = ParserState.object;
break;
}
case field: {
assert result != null;
assert fieldName != null;
ObjectNode on = (ObjectNode)result;
JsonNode jn = parseNode(jp, true);
on.set(fieldName, jn);
fieldName = null;
state = ParserState.object;
break;
}
case array: {
assert result != null;
assert fieldName == null;
ArrayNode an = (ArrayNode)result;
JsonNode jn = parseNode(jp, true);
an.add(jn);
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* END_OBJECT is returned when encountering '}'
* which signals ending of an Object value
*/
case END_OBJECT: {
switch(state) {
case object: {
assert result != null;
assert fieldName == null;
state = ParserState.done;
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* START_ARRAY is returned when encountering '['
* which signals starting of an Array value
*/
case START_ARRAY: {
switch(state) {
case start: {
assert result == null;
assert fieldName == null;
result = factory.arrayNode();
state = ParserState.array;
break;
}
case field: {
assert result != null;
assert fieldName != null;
ObjectNode on = (ObjectNode)result;
JsonNode jn = parseNode(jp, true);
on.set(fieldName, jn);
fieldName = null;
state = ParserState.object;
break;
}
case array: {
assert result != null;
assert fieldName == null;
ArrayNode an = (ArrayNode)result;
JsonNode jn = parseNode(jp, true);
an.add(jn);
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* END_ARRAY is returned when encountering ']'
* which signals ending of an Array value
*/
case END_ARRAY: {
switch(state) {
case array: {
assert result != null;
assert fieldName == null;
state = ParserState.done;
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* FIELD_NAME is returned when a String token is encountered
* as a field name (same lexical value, different function)
*/
case FIELD_NAME: {
fieldName = jp.getValueAsString();
switch(state) {
case object: {
assert result != null;
assert fieldName == null;
state = ParserState.field;
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* Placeholder token returned when the input source has a concept
* of embedded Object that are not accessible as usual structure
* (of starting with {@link #START_OBJECT}, having values, ending with
* {@link #END_OBJECT}), but as "raw" objects.
*<p>
* Note: this token is never returned by regular JSON readers, but
* only by readers that expose other kinds of source (like
* <code>JsonNode</code>-based JSON trees, Maps, Lists and such).
*/
case VALUE_EMBEDDED_OBJECT: {
throw new Exception("Token not supported: "+token);
}
/**
* VALUE_STRING is returned when a String token is encountered
* in value context (array element, field value, or root-level
* stand-alone value)
*/
case VALUE_STRING: {
switch(state) {
case start: {
assert result == null;
assert fieldName == null;
result = factory.textNode(jp.getValueAsString());
state = ParserState.done;
break;
}
case field: {
assert result != null;
assert fieldName != null;
ObjectNode on = (ObjectNode)result;
JsonNode jn = factory.textNode(jp.getValueAsString());
on.set(fieldName, jn);
fieldName = null;
state = ParserState.object;
break;
}
case array: {
assert result != null;
assert fieldName == null;
ArrayNode an = (ArrayNode)result;
JsonNode jn = factory.textNode(jp.getValueAsString());
an.add(jn);
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* VALUE_NUMBER_INT is returned when an integer numeric token is
* encountered in value context: that is, a number that does
* not have floating point or exponent marker in it (consists
* only of an optional sign, followed by one or more digits)
*/
case VALUE_NUMBER_INT: {
switch(state) {
case start: {
assert result == null;
assert fieldName == null;
result = factory.numberNode(jp.getLongValue());
state = ParserState.done;
break;
}
case field: {
assert result != null;
assert fieldName != null;
ObjectNode on = (ObjectNode)result;
JsonNode jn = factory.numberNode(jp.getLongValue());
on.set(fieldName, jn);
fieldName = null;
state = ParserState.object;
break;
}
case array: {
assert result != null;
assert fieldName == null;
ArrayNode an = (ArrayNode)result;
JsonNode jn = factory.numberNode(jp.getLongValue());
an.add(jn);
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* VALUE_NUMBER_INT is returned when a numeric token other
* that is not an integer is encountered: that is, a number that does
* have floating point or exponent marker in it, in addition
* to one or more digits.
*/
case VALUE_NUMBER_FLOAT: {
switch(state) {
case start: {
assert result == null;
assert fieldName == null;
result = factory.numberNode(jp.getDoubleValue());
state = ParserState.done;
break;
}
case field: {
assert result != null;
assert fieldName != null;
ObjectNode on = (ObjectNode)result;
JsonNode jn = factory.numberNode(jp.getDoubleValue());
on.set(fieldName, jn);
fieldName = null;
state = ParserState.object;
break;
}
case array: {
assert result != null;
assert fieldName == null;
ArrayNode an = (ArrayNode)result;
JsonNode jn = factory.numberNode(jp.getDoubleValue());
an.add(jn);
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* VALUE_TRUE is returned when encountering literal "true" in
* value context
*/
case VALUE_TRUE: {
switch(state) {
case start: {
assert result == null;
assert fieldName == null;
result = factory.booleanNode(true);
state = ParserState.done;
break;
}
case field: {
assert result != null;
assert fieldName != null;
ObjectNode on = (ObjectNode)result;
JsonNode jn = factory.booleanNode(true);
on.set(fieldName, jn);
fieldName = null;
state = ParserState.object;
break;
}
case array: {
assert result != null;
assert fieldName == null;
ArrayNode an = (ArrayNode)result;
JsonNode jn = factory.booleanNode(true);
an.add(jn);
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* VALUE_FALSE is returned when encountering literal "false" in
* value context
*/
case VALUE_FALSE: {
switch(state) {
case start: {
assert result == null;
assert fieldName == null;
result = factory.booleanNode(false);
state = ParserState.done;
break;
}
case field: {
assert result != null;
assert fieldName != null;
ObjectNode on = (ObjectNode)result;
JsonNode jn = factory.booleanNode(false);
on.set(fieldName, jn);
fieldName = null;
state = ParserState.object;
break;
}
case array: {
assert result != null;
assert fieldName == null;
ArrayNode an = (ArrayNode)result;
JsonNode jn = factory.booleanNode(false);
an.add(jn);
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
/**
* VALUE_NULL is returned when encountering literal "null" in
* value context
*/
case VALUE_NULL: {
switch(state) {
case start: {
assert result == null;
assert fieldName == null;
result = factory.nullNode();
state = ParserState.done;
break;
}
case field: {
assert result != null;
assert fieldName != null;
ObjectNode on = (ObjectNode)result;
JsonNode jn = factory.nullNode();
on.set(fieldName, jn);
fieldName = null;
state = ParserState.object;
break;
}
case array: {
assert result != null;
assert fieldName == null;
ArrayNode an = (ArrayNode)result;
JsonNode jn = factory.nullNode();
an.add(jn);
break;
}
default: {
throw new Exception("Unexpected state: "+state+", for token: "+token);
}
}
break;
}
default: {
throw new Exception("Token not supported: "+token);
}
}
if(state == ParserState.done) {
break;
}
}
return result;
}
}
}