英文:
Creating a parser of Class name + String value to a typed value
问题
我正在尝试编写一个方法,该方法可以接受一个字符串类名和一个字符串值,并将该字符串表示的值返回。
示例输入:
parse("java.lang.String", "abc") -> String "ABC"
parse("java.lang.Boolean", "FALSE") -> Boolean FALSE
parse("java.lang.Integer", "123") -> Integer 123
parse("com.me.Color", "RED") -> enum Color.RED
我发现,如果我使用一个包含`assignableFrom`调用的if块,我可以实现这一点。但我更希望编写一些更具扩展性的代码,这样明天就不会那么难添加一个新的解析器。
这是我现在的代码:
```java
String stringClassName = //stringified full class name
String value = //value to parse
Class<?> fieldType = Class.forName(stringClassName)
if (fieldType.isAssignableFrom(String.class)) {
return value;
} else if (fieldType.isAssignableFrom(Boolean.class)) {
return Util.toBoolean(value);
} else if (fieldType.isEnum()) {
return Util.toEnum(fieldType, value);
} else {
// throw exception
}
<details>
<summary>英文:</summary>
I am trying to write a method that can take in a String classname and a String value, and return the value represented as that String.
Example inputs:
parse("java.lang.String", "abc") -> String "ABC"
parse("java.lang.Boolean", "FALSE") -> Boolean FALSE
parse("java.lang.Integer", "123") -> Integer 123
parse("com.me.Color", "RED") -> enum Color.RED
I have found that if I use an if block containing `assignableFrom` calls, I can achieve this. But would prefer writing something more extendable, so it isn't as difficult to add a new parser tomorrow.
This is what I have now:
String stringClassName = //stringified full class name
String value = //value to parse
Class<?> fieldType = Class.forName(stringClassName)
if (fieldType.isAssignableFrom(String.class)) {
return value;
} else if (fieldType.isAssignableFrom(Boolean.class)) {
return Util.toBoolean(value);
} else if (fieldType.isEnum()) {
return Util.toEnum(fieldType, value);
} else {
// throw exception
}
</details>
# 答案1
**得分**: 2
以下是翻译好的代码部分:
```java
有多种方法可以做到这一点。例如:
您可以创建一个名为`Parser`的接口
```java
package example;
public interface Parser {
boolean canParse(String fullQualifiedClassName);
Object parse(String fullQualifiedClassName, String value) throws ParseException;
class ParseException extends Exception {
public ParseException(String msg) {
super(msg);
}
public ParseException(Exception cause) {
super(cause);
}
}
}
并且将所有默认实现放在一个枚举或以其他方式静态定义的地方:
package example;
public enum DefaultParser implements Parser {
STRING {
@Override
public boolean canParse(String fullQualifiedClassName) {
return isClassAssignableFromClassName(fullQualifiedClassName, String.class);
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
return value;
}
},
ENUM {
@Override
public boolean canParse(String fullQualifiedClassName) {
return isClassAssignableFromClassName(fullQualifiedClassName, Enum.class);
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
final Class<?> clazz;
try {
clazz = (Class<? extends Enum>) Class.forName(fullQualifiedClassName);
} catch (ClassNotFoundException e) {
throw new ParseException(e);
}
return Enum.valueOf(clazz, value);
}
},
BOOLEAN {
@Override
public boolean canParse(String fullQualifiedClassName) {
return isClassAssignableFromClassName(fullQualifiedClassName, Boolean.class);
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
return value.toLowerCase().equals("true");
}
};
private static boolean isClassAssignableFromClassName(String fullQualifiedClassName, Class<?> clazz) {
try {
return clazz.isAssignableFrom(Class.forName(fullQualifiedClassName));
} catch (ClassNotFoundException e) {
return false;
}
}
}
还有一个ParentParser
实现,将多个解析器组合成一个:
package example;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class ParentParser implements Parser {
private final List<Parser> parsers;
public ParentParser() {
this.parsers = new ArrayList<>();
this.parsers.addAll(Arrays.asList(DefaultParser.values()));
}
public void register(Parser parser) {
this.parsers.add(parser);
}
@Override
public boolean canParse(String fullQualifiedClassName) {
return findParser(fullQualifiedClassName).isPresent();
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
return findParser(fullQualifiedClassName)
.orElseThrow(() -> new ParseException("no registered parser found for class=" + fullQualifiedClassName))
.parse(fullQualifiedClassName, value);
}
private Optional<Parser> findParser(String fullQualifiedClassName) {
return this.parsers.stream().filter(parser -> parser.canParse(fullQualifiedClassName)).findAny();
}
}
然后可以像这样使用它:
package example;
import example.Parser.ParseException;
public class Example {
public static void main(String[] args) throws ParseException {
final ParentParser parser = new ParentParser();
System.out.println(parser.parse("java.lang.String", "hello world"));
System.out.println(parser.parse("java.lang.Boolean", "true"));
System.out.println(parser.parse("java.time.DayOfWeek", "TUESDAY"));
}
}
您还可以添加更多的解析器,例如使用Jackson(JSON)的解析器:
package example;
import com.fasterxml.jackson.databind.ObjectMapper;
import example.Parser.ParseException;
import java.io.IOException;
public class Example {
public static void main(String[] args) throws ParseException {
final ParentParser parser = new ParentParser();
System.out.println(parser.parse("java.lang.String", "hello world"));
System.out.println(parser.parse("java.lang.Boolean", "true"));
System.out.println(parser.parse("java.time.DayOfWeek", "TUESDAY"));
parser.register(new JacksonParser());
System.out.println(parser.parse("java.util.Map", "{\"key\":\"value\"}"));
}
private static class JacksonParser implements Parser {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public boolean canParse(String fullQualifiedClassName) {
final Class<?> clazz;
try {
clazz = Class.forName(fullQualifiedClassName);
} catch (ClassNotFoundException e) {
return false;
}
return MAPPER.canDeserialize(MAPPER.constructType(clazz));
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
try {
return MAPPER.readValue(value, Class.forName(fullQualifiedClassName));
} catch (ClassNotFoundException | IOException e) {
throw new ParseException(e);
}
}
}
}
请注意,根据您的需求,可以进行优化。如果您的解析器实现只能解析静态类型列表,并且每个类只有一个解析器实现,您应该将List<Parser>
更改为Map<Class<?>, Parser>
,并将register方法更改为register(Class<?>, Parser)
,例如。
英文:
There are multiple ways to do this. For example:
You could have an interface called Parser
package example;
public interface Parser {
boolean canParse(String fullQualifiedClassName);
Object parse(String fullQualifiedClassName, String value) throws ParseException;
class ParseException extends Exception {
public ParseException(String msg) {
super(msg);
}
public ParseException(Exception cause) {
super(cause);
}
}
}
And all your Default-Implementations in an Enum or statically defined in another way:
package example;
public enum DefaultParser implements Parser {
STRING {
@Override
public boolean canParse(String fullQualifiedClassName) {
return isClassAssignableFromClassName(fullQualifiedClassName, String.class);
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
return value;
}
},
ENUM {
@Override
public boolean canParse(String fullQualifiedClassName) {
return isClassAssignableFromClassName(fullQualifiedClassName, Enum.class);
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
final Class<? extends Enum> clazz;
try {
clazz = (Class<? extends Enum>) Class.forName(fullQualifiedClassName);
} catch (ClassNotFoundException e) {
throw new ParseException(e);
}
return Enum.valueOf(clazz, value);
}
},
BOOLEAN {
@Override
public boolean canParse(String fullQualifiedClassName) {
return isClassAssignableFromClassName(fullQualifiedClassName, Boolean.class);
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
return value.toLowerCase().equals("true");
}
};
private static boolean isClassAssignableFromClassName(String fullQualifiedClassName, Class<?> clazz) {
try {
return clazz.isAssignableFrom(Class.forName(fullQualifiedClassName));
} catch (ClassNotFoundException e) {
return false;
}
}
}
And a ParentParser
Implementation that combines multiple Parsers into one:
package example;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class ParentParser implements Parser {
private final List<Parser> parsers;
public ParentParser() {
this.parsers = new ArrayList<>();
this.parsers.addAll(Arrays.asList(DefaultParser.values()));
}
public void register(Parser parser) {
this.parsers.add(parser);
}
@Override
public boolean canParse(String fullQualifiedClassName) {
return findParser(fullQualifiedClassName).isPresent();
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
return findParser(fullQualifiedClassName)
.orElseThrow(() -> new ParseException("no registered parser found for class=" + fullQualifiedClassName))
.parse(fullQualifiedClassName, value);
}
private Optional<Parser> findParser(String fullQualifiedClassName) {
return this.parsers.stream().filter(parser -> parser.canParse(fullQualifiedClassName)).findAny();
}
}
Which you can then use like this:
package example;
import example.Parser.ParseException;
public class Example {
public static void main(String[] args) throws ParseException {
final ParentParser parser = new ParentParser();
System.out.println(parser.parse("java.lang.String", "hello world"));
System.out.println(parser.parse("java.lang.Boolean", "true"));
System.out.println(parser.parse("java.time.DayOfWeek", "TUESDAY"));
}
}
And you could add more parsers, for example a parser using Jackson (JSON):
package example;
import com.fasterxml.jackson.databind.ObjectMapper;
import example.Parser.ParseException;
import java.io.IOException;
public class Example {
public static void main(String[] args) throws ParseException {
final ParentParser parser = new ParentParser();
System.out.println(parser.parse("java.lang.String", "hello world"));
System.out.println(parser.parse("java.lang.Boolean", "true"));
System.out.println(parser.parse("java.time.DayOfWeek", "TUESDAY"));
parser.register(new JacksonParser());
System.out.println(parser.parse("java.util.Map", "{\"key\":\"value\"}"));
}
private static class JacksonParser implements Parser {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public boolean canParse(String fullQualifiedClassName) {
final Class<?> clazz;
try {
clazz = Class.forName(fullQualifiedClassName);
} catch (ClassNotFoundException e) {
return false;
}
return MAPPER.canDeserialize(MAPPER.constructType(clazz));
}
@Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
try {
return MAPPER.readValue(value, Class.forName(fullQualifiedClassName));
} catch (ClassNotFoundException | IOException e) {
throw new ParseException(e);
}
}
}
}
Note that this can of course be optimized depending on your needs.
If your Parser-Implementations can only parse a static List of Types and there is only one Parser-Implementation per Class, you should change the List<Parser>
to Map<Class<?>, Parser>
and change the register-Method to register(Class<?> clazz, Parser parser)
for example
答案2
得分: 0
以下是翻译好的内容:
你可以使用 Java 中的反射 API 编写一个通用解决方案。
这将减少大量的代码量并且更具可扩展性。
同时,枚举类型需要单独的处理。
我在下面的代码中涵盖了基本情况。
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
Object instance1 = parse("java.lang.String", "abc", false);
Object instance2 = parse("java.lang.Boolean", "FALSE", false);
Object instance3 = parse("java.lang.Integer", "123", false);
Object instance4 = parse("com.me.Color", "RED", true);
}
private static Object parse(String className, String argument, boolean isEnum) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
if (isEnum) {
Object value = Enum.valueOf((Class<? extends Enum>) Class.forName(className), argument);
//System.out.println(value);
return value;
} else {
return parse(className, new Object[]{argument}, isEnum);
}
}
private static Object parse(String className, Object[] arguments, boolean isEnum) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> clazz = Class.forName(className);
Constructor<?> ctor = clazz.getConstructor(String.class);
Object object = ctor.newInstance(arguments);
//System.out.println(object);
return object;
}
英文:
You can write a generic solution using reflection apis in java.
That would reduce a lot amount of code and would be more extensible.
Also not there is a separate processing required for enum types.
I have covered the basic cases in the code shown below.
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
Object instance1 = parse("java.lang.String", "abc", false);
Object instance2 = parse("java.lang.Boolean", "FALSE", false);
Object instance3 = parse("java.lang.Integer", "123", false);
Object instance4 = parse("com.me.Color", "RED", true);
}
private static Object parse(String className, String argument, boolean isEnum) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
if (isEnum) {
Object value = Enum.valueOf((Class<? extends Enum>) Class.forName(className), argument);
//System.out.println(value);
return value;
} else {
return parse(className, new Object[]{argument}, isEnum);
}
}
private static Object parse(String className, Object[] arguments, boolean isEnum) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> clazz = Class.forName(className);
Constructor<?> ctor = clazz.getConstructor(String.class);
Object object = ctor.newInstance(arguments);
//System.out.println(object);
return object;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论