是否有一种机制可以应用一组标准的检查来检测字符串,然后使用jackson的标准文本相关lib(csv、json甚至jackson core)将其转换为检测到的类型?我可以想象使用它和一个与该值相关联的标签(例如csv头)来做一些类似以下的事情:
JavaTypeAndValue typeAndValue = StringToJavaType.fromValue(Object x, String label);
typeAndValue.type() // FQN of Java type, maybe
typeAndValue.label() // where label might be a column header value, for example
typeAndValue.value() // returns Object of typeAndValue.type()
应用转换需要一组“提取器”,类的使用者必须意识到“object”返回类型的“模糊性”,但考虑到信息的用途,仍然能够消费和使用信息。
我目前正在考虑的示例涉及到构造sqlddl或dml,比如createtable语句,它使用从csv文件的行求值得到的列表中的信息。
经过更多的挖掘,希望找到一些东西在那里,我写了开始我的想法。
请记住,我在这里的意图不是要呈现一些“完整的”,因为我确信这里缺少一些东西,边缘案例没有解决,等等。
这个 pasrse(List<Map<String, String>> rows, List<String> headers
例如,这可能是从jackson读取的csv文件中的行样本。
再说一次,这是不完整的,所以我不打算挑出下面的所有错误。问题不是“我们该怎么写这个”,而是“有没有人熟悉这样的东西?”。
import gms.labs.cassandra.sandbox.extractors.Extractor;
import gms.labs.cassandra.sandbox.extractors.Extractors;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@Accessors(fluent=true, chain=true)
public class TypeAndValue
{
@Builder
TypeAndValue(Class<?> type, String rawValue){
this.type = type;
this.rawValue = rawValue;
label = "NONE";
}
@Getter
final Class<?> type;
@Getter
final String rawValue;
@Setter
@Getter
String label;
public Object value(){
return Extractors.extractorFor(this).value(rawValue);
}
static final String DEFAULT_LABEL = "NONE";
}
一个简单的解析器,其中 parse
来自一个我有 List<Map<String,String>>
从CSV头目那里。
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.math.NumberUtils;
import java.util.*;
import java.util.function.BiFunction;
public class JavaTypeParser
{
public static final List<TypeAndValue> parse(List<Map<String, String>> rows, List<String> headers)
{
List<TypeAndValue> typesAndVals = new ArrayList<TypeAndValue>();
for (Map<String, String> row : rows) {
for (String header : headers) {
String val = row.get(header);
TypeAndValue typeAndValue =
// isNull, isBoolean, isNumber
isNull(val).orElse(isBoolean(val).orElse(isNumber(val).orElse(_typeAndValue.apply(String.class, val).get())));
typesAndVals.add(typeAndValue.label(header));
}
}
}
public static Optional<TypeAndValue> isNumber(String val)
{
if (!NumberUtils.isCreatable(val)) {
return Optional.empty();
} else {
return _typeAndValue.apply(NumberUtils.createNumber(val).getClass(), val);
}
}
public static Optional<TypeAndValue> isBoolean(String val)
{
boolean bool = (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"));
if (bool) {
return _typeAndValue.apply(Boolean.class, val);
} else {
return Optional.empty();
}
}
public static Optional<TypeAndValue> isNull(String val){
if(Objects.isNull(val) || val.equals("null")){
return _typeAndValue.apply(ObjectUtils.Null.class,val);
}
else{
return Optional.empty();
}
}
static final BiFunction<Class<?>, String, Optional<TypeAndValue>> _typeAndValue = (type, value) -> Optional.of(
TypeAndValue.builder().type(type).rawValue(value).build());
}
提取器。这只是一个示例,说明如何将值(包含在字符串中)的“提取器”注册到某个地方进行查找。它们也可以通过其他任何方式被引用。
import gms.labs.cassandra.sandbox.TypeAndValue;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.math.NumberUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
public class Extractors
{
private static final List<Class> NUMS = Arrays.asList(
BigInteger.class,
BigDecimal.class,
Long.class,
Integer.class,
Double.class,
Float.class);
public static final Extractor<?> extractorFor(TypeAndValue typeAndValue)
{
if (NUMS.contains(typeAndValue.type())) {
return (Extractor<Number>) value -> NumberUtils.createNumber(value);
} else if(typeAndValue.type().equals(Boolean.class)) {
return (Extractor<Boolean>) value -> Boolean.valueOf(value);
} else if(typeAndValue.type().equals(ObjectUtils.Null.class)) {
return (Extractor<ObjectUtils.Null>) value -> null; // should we just return the raw value. some frameworks coerce to null.
} else if(typeAndValue.type().equals(String.class)) {
return (Extractor<String>) value -> typeAndValue.rawValue(); // just return the raw value. some frameworks coerce to null.
}
else{
throw new RuntimeException("unsupported");
}
}
}
我在javatypeparser类中运行了这个,以供参考。
public static void main(String[] args)
{
Optional<TypeAndValue> num = isNumber("-1230980980980980980980980980980988009808989080989809890808098292");
num.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass()); // BigInteger
});
num = isNumber("-123098098097987");
num.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass()); // Long
});
num = isNumber("-123098.098097987"); // Double
num.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass());
});
num = isNumber("-123009809890898.0980979098098908080987"); // BigDecimal
num.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass());
});
Optional<TypeAndValue> bool = isBoolean("FaLse");
bool.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass()); // Boolean
});
Optional<TypeAndValue> nulll = isNull("null");
nulll.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
//System.out.println(typeAndVal.value().getClass()); would throw null pointer exception
System.out.println(typeAndVal.type()); // ObjectUtils.Null (from apache commons lang3)
});
}
3条答案
按热度按时间tmb3ates1#
实际上,您要做的是编写一个解析器。将片段转换为解析树。解析树捕获类型和值。对于层次结构类型(如数组和对象),每个树节点都包含子节点。
最常用的解析器之一是antlr(尽管对于您的用例来说有点过分了)。antlr提供了对json的现成支持。
我建议您花点时间来理解所有涉及到的概念。尽管一开始看起来有些过分,但当您进行任何类型的扩展时,都会很快得到回报。改变语法相对容易;生成的代码相当复杂。此外,所有解析器生成器都会验证语法以显示逻辑错误。
当然,如果您将自己局限于只解析csv或json(而不是同时解析两者),那么您应该使用现有库的解析器。例如,jackson有objectmapper.readtree来获取解析树。你也可以用
ObjectMapper.readValue(<fragment>, Object.class)
只需获取规范的java类。6ioyuze22#
试试这个:
xcitsw883#
我不知道有哪一个库可以这样做,也从未见过任何东西在一组可能的开放类型上以这种方式工作。
对于封闭的类型集(您知道所有可能的输出类型),更简单的方法是将类fqn写入字符串中(根据您的描述,如果您控制写入的字符串,我没有得到)。
完整的fqn或其别名。
否则我想没有办法不写所有的支票。
此外,它将是非常微妙的,因为我正在考虑的边缘用例。
假设您在字符串中使用json作为序列化格式,您将如何区分
String
价值观Hello World
和一个Date
以某种iso格式编写(例如。2020-09-22
). 为此,您需要在所做的检查中引入一些优先级(首先尝试使用一些regex检查它是否是日期,如果不是,则使用下一个,简单的字符串one是最后一个)如果有两个对象:
并且您将收到第二种类型的序列化值,但薪水为null(null或属性完全丢失)。
你怎么区分一个集合和一个列表?
我不知道你想要的是如此动态,或者你已经知道所有可能的反序列化类型,也许问题中的更多细节会有所帮助。
更新
刚看到密码,现在好像更清楚了。如果你知道所有可能的输出,那就是方法。
我要做的唯一的改变,就是简化您想要管理的类型的增加,抽象提取过程。
为了做到这一点,我认为应该做一些小的改变,比如:
然后可以为每种类型定义一个提取器:
然后登记并自动检查:
我建议你https://commons.apache.org/proper/commons-csv 因为csv解析可能会引发一些棘手的问题。