使用字符串输入解析自定义对象
Parse custom Objects with string input
我想解析我有输入值的对象。该对象有子类。
我不想在要解析字符串时创建实例。
但是我想覆盖方法 'parse'.
class A {
public A(object param) {
//code
}
public A parse(String input) {
//code
}
}
class B extends A {
public B parse(String input) {
//code
}
}
当我解析我想要的对象时,用反射来做:
A newObject = Class.forName(className).getMethod("parse", myParseText);
有什么好的方法吗
为了获得最大的灵活性,我将解析代码移出到一个单独的解析器中 class。例如下面的代码是我自己在一个项目中使用的解析器 class 的简化实现:
public final class ParseUtil {
public interface IStringParser<E> {
public E parse(String s) throws ParseException;
}
private ParseUtil() {}
private static Map<Class<?>, IStringParser<?>> STRING_PARSERS = new ConcurrentHashMap<>();
public static <E> void registerStringParser(Class<E> c, IStringParser<E> p){
STRING_PARSERS.put(c, p);
}
@SuppressWarnings("unchecked")
public static <E> IStringParser<E> getStringParser(Class<E> c){
return (IStringParser<E>) STRING_PARSERS.get(c);
}
public static <E> E parse(String s, Class<E> clazz) throws ParseException{
if (s == null || s.length() == 0 || clazz == null) {
throw new IllegalArgumentException();
}
IStringParser<E> stringParser = getStringParser(clazz);
if (stringParser == null) {
throw new ParseException(clazz);
}
return stringParser.parse(s);
}
}
然后可以将任意 classes 的 IStringParser
实现注册到 ParserUtil
,如下所示:
ParseUtil.registerStringParser(File.class, new IStringParser<File>() {
@Override
public File parse(String s)throws ParseException {
return new File(s);
}
});
或使用 lambda:ParseUtil.registerStringParser(File.class, s -> new File(s));
调用者有责任决定如何解析 class 以及 IStringParser
何时为 registered/unregistered。
通过将解析代码从 class 本身移开,稍后只需注册一个不同的 IStringParser
或更改 parse
方法即可轻松更改实现ParseUtil
class。例如在我自己的项目中,我使用以下解析方法,它为众所周知的 Java classes 提供了一些合理的默认值,并使用 Gson 解析没有其他 IStringParser
的对象已注册:
public static <E> E parse(String s, Class<E> clazz) throws ParseException{
if (s == null || s.length() == 0 || clazz == null) {
throw new IllegalArgumentException();
}
IStringParser<E> stringParser = getStringParser(clazz);
if (stringParser != null) {
return stringParser.parse(s);
}
if (Number.class.isAssignableFrom(clazz)) {
// simple number
try {
if (clazz == Integer.class) {
return clazz.cast(Integer.parseInt(s));
}
if (clazz == Long.class) {
return clazz.cast(Long.parseLong(s));
}
if (clazz == Double.class) {
return clazz.cast(Double.parseDouble(s));
}
if (clazz == Float.class) {
return clazz.cast(Float.parseFloat(s));
}
if (clazz == Short.class) {
return clazz.cast(Short.parseShort(s));
}
if (clazz == Byte.class) {
return clazz.cast(Byte.parseByte(s));
}
} catch (NumberFormatException e) {
throw new ParseException(clazz, e);
}
}
if (clazz == String.class) {
return clazz.cast(s);
}
if (clazz == Character.class) {
if (s.length() == 1) {
return clazz.cast(s.charAt(0));
} else{
throw new ParseException("Unable to parse Character \"" + s + "\"");
}
}
if (clazz == Boolean.class) {
switch (s) {
case "true":
return clazz.cast(Boolean.TRUE);
case "false":
return clazz.cast(Boolean.FALSE);
case "1":
return clazz.cast(Boolean.TRUE);
case "0":
return clazz.cast(Boolean.FALSE);
default:
throw new ParseException("Unable to parse boolean \"" + s + "\"");
}
}
if (clazz == Class.class) {
try {
return clazz.cast(Class.forName(s));
} catch (ClassNotFoundException e) {
throw new ParseException(clazz, e);
}
}
if (Enum.class.isAssignableFrom(clazz)) {
@SuppressWarnings({ "rawtypes" })
Class c = (Class)clazz;
@SuppressWarnings("unchecked")
Object o = Enum.valueOf(c, s);
return clazz.cast(o);
}
E result = null;
try {
// if all else fails use Gson to parse the class
result = getGSON().fromJson(s, clazz);
} catch (JsonSyntaxException e) {
throw new ParseException(clazz. e);
}
if (result == null) {
throw new ParseException(clazz);
}
return result;
}
我想解析我有输入值的对象。该对象有子类。
我不想在要解析字符串时创建实例。 但是我想覆盖方法 'parse'.
class A {
public A(object param) {
//code
}
public A parse(String input) {
//code
}
}
class B extends A {
public B parse(String input) {
//code
}
}
当我解析我想要的对象时,用反射来做:
A newObject = Class.forName(className).getMethod("parse", myParseText);
有什么好的方法吗
为了获得最大的灵活性,我将解析代码移出到一个单独的解析器中 class。例如下面的代码是我自己在一个项目中使用的解析器 class 的简化实现:
public final class ParseUtil {
public interface IStringParser<E> {
public E parse(String s) throws ParseException;
}
private ParseUtil() {}
private static Map<Class<?>, IStringParser<?>> STRING_PARSERS = new ConcurrentHashMap<>();
public static <E> void registerStringParser(Class<E> c, IStringParser<E> p){
STRING_PARSERS.put(c, p);
}
@SuppressWarnings("unchecked")
public static <E> IStringParser<E> getStringParser(Class<E> c){
return (IStringParser<E>) STRING_PARSERS.get(c);
}
public static <E> E parse(String s, Class<E> clazz) throws ParseException{
if (s == null || s.length() == 0 || clazz == null) {
throw new IllegalArgumentException();
}
IStringParser<E> stringParser = getStringParser(clazz);
if (stringParser == null) {
throw new ParseException(clazz);
}
return stringParser.parse(s);
}
}
然后可以将任意 classes 的 IStringParser
实现注册到 ParserUtil
,如下所示:
ParseUtil.registerStringParser(File.class, new IStringParser<File>() {
@Override
public File parse(String s)throws ParseException {
return new File(s);
}
});
或使用 lambda:ParseUtil.registerStringParser(File.class, s -> new File(s));
调用者有责任决定如何解析 class 以及 IStringParser
何时为 registered/unregistered。
通过将解析代码从 class 本身移开,稍后只需注册一个不同的 IStringParser
或更改 parse
方法即可轻松更改实现ParseUtil
class。例如在我自己的项目中,我使用以下解析方法,它为众所周知的 Java classes 提供了一些合理的默认值,并使用 Gson 解析没有其他 IStringParser
的对象已注册:
public static <E> E parse(String s, Class<E> clazz) throws ParseException{
if (s == null || s.length() == 0 || clazz == null) {
throw new IllegalArgumentException();
}
IStringParser<E> stringParser = getStringParser(clazz);
if (stringParser != null) {
return stringParser.parse(s);
}
if (Number.class.isAssignableFrom(clazz)) {
// simple number
try {
if (clazz == Integer.class) {
return clazz.cast(Integer.parseInt(s));
}
if (clazz == Long.class) {
return clazz.cast(Long.parseLong(s));
}
if (clazz == Double.class) {
return clazz.cast(Double.parseDouble(s));
}
if (clazz == Float.class) {
return clazz.cast(Float.parseFloat(s));
}
if (clazz == Short.class) {
return clazz.cast(Short.parseShort(s));
}
if (clazz == Byte.class) {
return clazz.cast(Byte.parseByte(s));
}
} catch (NumberFormatException e) {
throw new ParseException(clazz, e);
}
}
if (clazz == String.class) {
return clazz.cast(s);
}
if (clazz == Character.class) {
if (s.length() == 1) {
return clazz.cast(s.charAt(0));
} else{
throw new ParseException("Unable to parse Character \"" + s + "\"");
}
}
if (clazz == Boolean.class) {
switch (s) {
case "true":
return clazz.cast(Boolean.TRUE);
case "false":
return clazz.cast(Boolean.FALSE);
case "1":
return clazz.cast(Boolean.TRUE);
case "0":
return clazz.cast(Boolean.FALSE);
default:
throw new ParseException("Unable to parse boolean \"" + s + "\"");
}
}
if (clazz == Class.class) {
try {
return clazz.cast(Class.forName(s));
} catch (ClassNotFoundException e) {
throw new ParseException(clazz, e);
}
}
if (Enum.class.isAssignableFrom(clazz)) {
@SuppressWarnings({ "rawtypes" })
Class c = (Class)clazz;
@SuppressWarnings("unchecked")
Object o = Enum.valueOf(c, s);
return clazz.cast(o);
}
E result = null;
try {
// if all else fails use Gson to parse the class
result = getGSON().fromJson(s, clazz);
} catch (JsonSyntaxException e) {
throw new ParseException(clazz. e);
}
if (result == null) {
throw new ParseException(clazz);
}
return result;
}