创建一个解析器,将类名和字符串值解析为类型化的值

huangapple go评论60阅读模式
英文:

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&#39;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&lt;?&gt; 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&lt;? extends Enum&gt; clazz;
			try {
				clazz = (Class&lt;? extends Enum&gt;) 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(&quot;true&quot;);
		}
	};

	private static boolean isClassAssignableFromClassName(String fullQualifiedClassName, Class&lt;?&gt; 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&lt;Parser&gt; parsers;

	public ParentParser() {
		this.parsers = new ArrayList&lt;&gt;();
		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(() -&gt; new ParseException(&quot;no registered parser found for class=&quot; + fullQualifiedClassName))
			  .parse(fullQualifiedClassName, value);
	}

	private Optional&lt;Parser&gt; findParser(String fullQualifiedClassName) {
		return this.parsers.stream().filter(parser -&gt; 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(&quot;java.lang.String&quot;, &quot;hello world&quot;));
		System.out.println(parser.parse(&quot;java.lang.Boolean&quot;, &quot;true&quot;));
		System.out.println(parser.parse(&quot;java.time.DayOfWeek&quot;, &quot;TUESDAY&quot;));
	}
}

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(&quot;java.lang.String&quot;, &quot;hello world&quot;));
		System.out.println(parser.parse(&quot;java.lang.Boolean&quot;, &quot;true&quot;));
		System.out.println(parser.parse(&quot;java.time.DayOfWeek&quot;, &quot;TUESDAY&quot;));

		parser.register(new JacksonParser());

		System.out.println(parser.parse(&quot;java.util.Map&quot;, &quot;{\&quot;key\&quot;:\&quot;value\&quot;}&quot;));
	}

	private static class JacksonParser implements Parser {

		private static final ObjectMapper MAPPER = new ObjectMapper();

		@Override
		public boolean canParse(String fullQualifiedClassName) {
			final Class&lt;?&gt; 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&lt;Parser&gt; to Map&lt;Class&lt;?&gt;, Parser&gt; and change the register-Method to register(Class&lt;?&gt; 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(&quot;java.lang.String&quot;, &quot;abc&quot;, false);
Object instance2 = parse(&quot;java.lang.Boolean&quot;, &quot;FALSE&quot;, false);
Object instance3 = parse(&quot;java.lang.Integer&quot;, &quot;123&quot;, false);
Object instance4 = parse(&quot;com.me.Color&quot;, &quot;RED&quot;, true);
}
private static Object parse(String className, String argument, boolean isEnum) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
if (isEnum) {
Object value = Enum.valueOf((Class&lt;? extends Enum&gt;) 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&lt;?&gt; clazz = Class.forName(className);
Constructor&lt;?&gt; ctor = clazz.getConstructor(String.class);
Object object = ctor.newInstance(arguments);
//System.out.println(object);
return object;
}

huangapple
  • 本文由 发表于 2020年4月8日 00:21:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/61084662.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定