Skip to content

Commit

Permalink
Added smart data parsing tool that detects JSON, XML or YAML data for…
Browse files Browse the repository at this point in the history
…mat.
  • Loading branch information
nmihajlovski committed Oct 12, 2015
1 parent 9ca85dc commit 74b336e
Show file tree
Hide file tree
Showing 23 changed files with 255 additions and 31 deletions.
Expand Up @@ -29,6 +29,7 @@
import org.rapidoid.annotation.Since; import org.rapidoid.annotation.Since;
import org.rapidoid.cls.Cls; import org.rapidoid.cls.Cls;
import org.rapidoid.config.Conf; import org.rapidoid.config.Conf;
import org.rapidoid.data.JSON;
import org.rapidoid.dispatch.DispatchResult; import org.rapidoid.dispatch.DispatchResult;
import org.rapidoid.dispatch.PojoDispatchException; import org.rapidoid.dispatch.PojoDispatchException;
import org.rapidoid.dispatch.PojoDispatcher; import org.rapidoid.dispatch.PojoDispatcher;
Expand All @@ -39,7 +40,6 @@
import org.rapidoid.http.HttpExchange; import org.rapidoid.http.HttpExchange;
import org.rapidoid.http.HttpExchangeImpl; import org.rapidoid.http.HttpExchangeImpl;
import org.rapidoid.io.Res; import org.rapidoid.io.Res;
import org.rapidoid.jackson.JSON;
import org.rapidoid.plugins.templates.Templates; import org.rapidoid.plugins.templates.Templates;
import org.rapidoid.util.U; import org.rapidoid.util.U;
import org.rapidoid.util.UTILS; import org.rapidoid.util.UTILS;
Expand Down
2 changes: 1 addition & 1 deletion rapidoid-beany/src/main/java/org/rapidoid/beany/Beany.java
Expand Up @@ -22,8 +22,8 @@
import org.rapidoid.cls.Cls; import org.rapidoid.cls.Cls;
import org.rapidoid.cls.Proxies; import org.rapidoid.cls.Proxies;
import org.rapidoid.cls.TypeKind; import org.rapidoid.cls.TypeKind;
import org.rapidoid.data.JSON;
import org.rapidoid.dates.Dates; import org.rapidoid.dates.Dates;
import org.rapidoid.jackson.JSON;
import org.rapidoid.lambda.Mapper; import org.rapidoid.lambda.Mapper;
import org.rapidoid.log.Log; import org.rapidoid.log.Log;
import org.rapidoid.util.U; import org.rapidoid.util.U;
Expand Down
2 changes: 1 addition & 1 deletion rapidoid-docs/src/main/java/org/rapidoid/docs/Docs.java
Expand Up @@ -29,10 +29,10 @@
import org.rapidoid.app.AppHandler; import org.rapidoid.app.AppHandler;
import org.rapidoid.config.Config; import org.rapidoid.config.Config;
import org.rapidoid.ctx.Classes; import org.rapidoid.ctx.Classes;
import org.rapidoid.data.JSON;
import org.rapidoid.http.HTTP; import org.rapidoid.http.HTTP;
import org.rapidoid.http.HttpException; import org.rapidoid.http.HttpException;
import org.rapidoid.io.IO; import org.rapidoid.io.IO;
import org.rapidoid.jackson.JSON;
import org.rapidoid.main.Rapidoid; import org.rapidoid.main.Rapidoid;
import org.rapidoid.plugins.templates.Templates; import org.rapidoid.plugins.templates.Templates;
import org.rapidoid.scan.ClasspathUtil; import org.rapidoid.scan.ClasspathUtil;
Expand Down
Expand Up @@ -30,11 +30,11 @@
import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since; import org.rapidoid.annotation.Since;
import org.rapidoid.cls.Cls; import org.rapidoid.cls.Cls;
import org.rapidoid.data.JSON;
import org.rapidoid.html.CustomTag; import org.rapidoid.html.CustomTag;
import org.rapidoid.html.HTML; import org.rapidoid.html.HTML;
import org.rapidoid.html.Tag; import org.rapidoid.html.Tag;
import org.rapidoid.html.TagWidget; import org.rapidoid.html.TagWidget;
import org.rapidoid.jackson.JSON;
import org.rapidoid.util.Constants; import org.rapidoid.util.Constants;
import org.rapidoid.util.U; import org.rapidoid.util.U;
import org.rapidoid.var.Var; import org.rapidoid.var.Var;
Expand Down
Expand Up @@ -22,9 +22,9 @@


import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since; import org.rapidoid.annotation.Since;
import org.rapidoid.data.JSON;
import org.rapidoid.http.session.InMemorySessionStore; import org.rapidoid.http.session.InMemorySessionStore;
import org.rapidoid.http.session.SessionStore; import org.rapidoid.http.session.SessionStore;
import org.rapidoid.jackson.JSON;
import org.rapidoid.net.Protocol; import org.rapidoid.net.Protocol;
import org.rapidoid.net.impl.RapidoidServerLoop; import org.rapidoid.net.impl.RapidoidServerLoop;
import org.rapidoid.webapp.WebAppGroup; import org.rapidoid.webapp.WebAppGroup;
Expand Down
Expand Up @@ -36,13 +36,13 @@
import org.rapidoid.ctx.UserInfo; import org.rapidoid.ctx.UserInfo;
import org.rapidoid.data.BinaryMultiData; import org.rapidoid.data.BinaryMultiData;
import org.rapidoid.data.Data; import org.rapidoid.data.Data;
import org.rapidoid.data.JSON;
import org.rapidoid.data.KeyValueRanges; import org.rapidoid.data.KeyValueRanges;
import org.rapidoid.data.MultiData; import org.rapidoid.data.MultiData;
import org.rapidoid.data.Range; import org.rapidoid.data.Range;
import org.rapidoid.data.Ranges; import org.rapidoid.data.Ranges;
import org.rapidoid.http.session.SessionStore; import org.rapidoid.http.session.SessionStore;
import org.rapidoid.io.Res; import org.rapidoid.io.Res;
import org.rapidoid.jackson.JSON;
import org.rapidoid.log.Log; import org.rapidoid.log.Log;
import org.rapidoid.mime.MediaType; import org.rapidoid.mime.MediaType;
import org.rapidoid.net.impl.ConnState; import org.rapidoid.net.impl.ConnState;
Expand Down
Expand Up @@ -27,10 +27,10 @@
import org.rapidoid.buffer.Buf; import org.rapidoid.buffer.Buf;
import org.rapidoid.bytes.Bytes; import org.rapidoid.bytes.Bytes;
import org.rapidoid.bytes.BytesUtil; import org.rapidoid.bytes.BytesUtil;
import org.rapidoid.data.JSON;
import org.rapidoid.data.KeyValueRanges; import org.rapidoid.data.KeyValueRanges;
import org.rapidoid.data.Range; import org.rapidoid.data.Range;
import org.rapidoid.data.Ranges; import org.rapidoid.data.Ranges;
import org.rapidoid.jackson.JSON;
import org.rapidoid.log.Log; import org.rapidoid.log.Log;
import org.rapidoid.net.impl.RapidoidHelper; import org.rapidoid.net.impl.RapidoidHelper;
import org.rapidoid.util.Constants; import org.rapidoid.util.Constants;
Expand Down
Expand Up @@ -29,12 +29,12 @@
import org.rapidoid.bufstruct.BufMapImpl; import org.rapidoid.bufstruct.BufMapImpl;
import org.rapidoid.bytes.Bytes; import org.rapidoid.bytes.Bytes;
import org.rapidoid.bytes.BytesUtil; import org.rapidoid.bytes.BytesUtil;
import org.rapidoid.data.JSON;
import org.rapidoid.data.KeyValueRanges; import org.rapidoid.data.KeyValueRanges;
import org.rapidoid.data.Range; import org.rapidoid.data.Range;
import org.rapidoid.data.Ranges; import org.rapidoid.data.Ranges;
import org.rapidoid.dates.Dates; import org.rapidoid.dates.Dates;
import org.rapidoid.http.HttpParser; import org.rapidoid.http.HttpParser;
import org.rapidoid.jackson.JSON;
import org.rapidoid.net.Protocol; import org.rapidoid.net.Protocol;
import org.rapidoid.net.abstracts.Channel; import org.rapidoid.net.abstracts.Channel;
import org.rapidoid.net.impl.RapidoidHelper; import org.rapidoid.net.impl.RapidoidHelper;
Expand Down
Expand Up @@ -23,9 +23,9 @@
import org.junit.Test; import org.junit.Test;
import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since; import org.rapidoid.annotation.Since;
import org.rapidoid.data.YAML;
import org.rapidoid.http.HttpTestCommons; import org.rapidoid.http.HttpTestCommons;
import org.rapidoid.io.Res; import org.rapidoid.io.Res;
import org.rapidoid.jackson.YAML;


@Authors("Nikolche Mihajlovski") @Authors("Nikolche Mihajlovski")
@Since("4.1.0") @Since("4.1.0")
Expand Down
@@ -1,4 +1,4 @@
package org.rapidoid.jackson; package org.rapidoid.data;


import java.io.OutputStream; import java.io.OutputStream;
import java.util.Map; import java.util.Map;
Expand Down
138 changes: 138 additions & 0 deletions rapidoid-jackson/src/main/java/org/rapidoid/data/Parse.java
@@ -0,0 +1,138 @@
package org.rapidoid.data;

/*
* #%L
* rapidoid-jackson
* %%
* Copyright (C) 2014 - 2015 Nikolche Mihajlovski and contributors
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

import org.rapidoid.util.U;

/**
* @author Nikolche Mihajlovski
* @since 4.4.0
*/
public class Parse {

enum DataFormat {
JSON, XML, YAML
};

private static final DataFormat[] XML_FIRST = { DataFormat.XML, DataFormat.JSON, DataFormat.YAML };

private static final DataFormat[] JSON_FIRST = { DataFormat.JSON, DataFormat.XML, DataFormat.YAML };

private static final DataFormat[] YAML_FIRST = { DataFormat.YAML, DataFormat.JSON, DataFormat.XML };

public static <T> T data(String data, Class<T> targetType) {
return data(data.getBytes(), targetType);
}

@SuppressWarnings("unchecked")
public static <T> T data(byte[] data, Class<T> targetType) {
if (data == null) {
return null;
}

// don't parse if the target type is byte[]
if (targetType == byte[].class) {
return (T) data;
}

DataFormat[] formatsOrder = detectDataFormat(data);

return tryToParseData(data, formatsOrder, targetType);
}

private static <T> T tryToParseData(byte[] data, DataFormat[] formatsOrder, Class<T> targetType) {

Exception firstException = null;

for (DataFormat dataFormat : formatsOrder) {
switch (dataFormat) {
case JSON:
try {
return JSON.parse(data, targetType);
} catch (Exception e) {
if (firstException == null) {
firstException = e;
}
}
break;

case XML:
try {
return XML.parse(data, targetType);
} catch (Exception e) {
if (firstException == null) {
firstException = e;
}
}
break;

case YAML:
try {
return YAML.parse(data, targetType);
} catch (Exception e) {
if (firstException == null) {
firstException = e;
}
}
break;

default:
break;
}
}

throw U.rte("Not a valid JSON, XML nor YAML format!", firstException);
}

static DataFormat[] detectDataFormat(byte[] data) {
if (data.length == 0) {
return YAML_FIRST;
}

byte start = findFirstNonWhitespaceByte(data);

if (start == '<') {
return XML_FIRST;
} else if (start == ' ' || (start == '-' && dataStartsWithYAMLMark(data))) {
return YAML_FIRST;
} else {
return JSON_FIRST;
}
}

private static byte findFirstNonWhitespaceByte(byte[] data) {
int i = 0;

while (Character.isWhitespace(data[i])) {
i++;
if (i >= data.length) {
return ' ';
}
}

return data[i];
}

private static boolean dataStartsWithYAMLMark(byte[] data) {
return data.length >= 3 && data[0] == '-' && data[1] == '-' && data[2] == '-';
}

}
@@ -1,4 +1,4 @@
package org.rapidoid.xml; package org.rapidoid.data;


/* /*
* #%L * #%L
Expand Down
@@ -1,4 +1,4 @@
package org.rapidoid.jackson; package org.rapidoid.data;


import java.io.OutputStream; import java.io.OutputStream;
import java.util.Map; import java.util.Map;
Expand Down
@@ -1,4 +1,4 @@
package org.rapidoid.jackson; package org.rapidoid.data;


/* /*
* #%L * #%L
Expand All @@ -23,6 +23,7 @@
import java.util.Map; import java.util.Map;


import org.junit.Test; import org.junit.Test;
import org.rapidoid.data.JSON;
import org.rapidoid.test.TestCommons; import org.rapidoid.test.TestCommons;


/** /**
Expand All @@ -34,12 +35,12 @@ public class JSONTest extends TestCommons {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void json() { public void json() {
Person p = new Person("john", 25); User p = new User("john", 25);


String json = JSON.stringify(p); String json = JSON.stringify(p);
System.out.println(json); System.out.println(json);


Person p2 = JSON.parse(json, Person.class); User p2 = JSON.parse(json, User.class);
eq(p2.name, p.name); eq(p2.name, p.name);
eq(p2.age, p.age); eq(p2.age, p.age);


Expand Down
83 changes: 83 additions & 0 deletions rapidoid-jackson/src/test/java/org/rapidoid/data/ParseTest.java
@@ -0,0 +1,83 @@
package org.rapidoid.data;

/*
* #%L
* rapidoid-jackson
* %%
* Copyright (C) 2014 - 2015 Nikolche Mihajlovski and contributors
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

import org.junit.Test;
import org.rapidoid.data.Parse.DataFormat;
import org.rapidoid.test.TestCommons;

/**
* @author Nikolche Mihajlovski
* @since 4.4.0
*/
public class ParseTest extends TestCommons {

@Test
public void testXMLParse() {
String xml = XML.stringify(new Person("abc", 123));
System.out.println(xml);

Person p = Parse.data(xml, Person.class);

eq(p.getName(), "abc");
eq(p.getAge(), 123);
}

@Test
public void testJSONParse() {
String json = JSON.stringify(new Person("abc", 123));
System.out.println(json);

Person p = Parse.data(json, Person.class);

eq(p.getName(), "abc");
eq(p.getAge(), 123);

isNull(Parse.data("null", Person.class));
eq(Parse.data("3", Integer.class).intValue(), 3);
isTrue(Parse.data("true", Boolean.class).booleanValue());
isFalse(Parse.data("false", Boolean.class).booleanValue());
eq(Parse.data("\"123\"", String.class), "123");
}

@Test
public void testYAMLParse() {
String yaml = YAML.stringify(new Person("abc", 123));
System.out.println(yaml);

Person p = Parse.data(yaml, Person.class);

eq(p.getName(), "abc");
eq(p.getAge(), 123);
}

@Test
public void testDataFormatAutoDetect() {
eq(Parse.detectDataFormat("---\n".getBytes())[0], DataFormat.YAML);
eq(Parse.detectDataFormat("".getBytes())[0], DataFormat.YAML);
eq(Parse.detectDataFormat("<abc>".getBytes())[0], DataFormat.XML);
eq(Parse.detectDataFormat("-1".getBytes())[0], DataFormat.JSON);
eq(Parse.detectDataFormat("-12345".getBytes())[0], DataFormat.JSON);
eq(Parse.detectDataFormat("null".getBytes())[0], DataFormat.JSON);
eq(Parse.detectDataFormat("\"fff\"".getBytes())[0], DataFormat.JSON);
}

}
@@ -1,4 +1,4 @@
package org.rapidoid.xml; package org.rapidoid.data;


/* /*
* #%L * #%L
Expand Down

0 comments on commit 74b336e

Please sign in to comment.