我有以下Json Schema。
{
"name":{
"first":{
"attributeValue":"firstName",
"attributeType":1,
"dataType":1
},
"last":{
"attributeValue":"lastName",
"attributeType":1,
"dataType":1
}
},
"age":{
"attributeValue":"age",
"attributeType":1,
"dataType":2
},
"address":{
"number":{
"attributeValue":"number",
"attributeType":1,
"dataType":1
},
"street":{
"attributeValue":"street",
"attributeType":1,
"dataType":1
},
"city":{
"attributeValue":"city",
"attributeType":1,
"dataType":1
},
"country":{
"attributeValue":"country",
"attributeType":1,
"dataType":1
}
},
"fullName":{
"attributeValue":"#firstName.concat(' ').concat(#lastName)",
"attributeType":2,
"dataType":1
}
}
在这里,具有attributeValue,dataType和attributeType节点的每个节点都被称为“Field”。每个其他父节点都是一个处理程序。 “name”是具有“first”和“last”字段的处理程序。但是对于年龄,由于没有父键,因此应该创建一个名为“age”的处理程序,并且应该添加一个字段“age”。处理程序可以有处理程序。处理程序中包含字段。下面是Handler对象表示。
public interface Handler<T> {
void addField(Field field);
void addHandler(Handler handler);
String getName();
List<Field> getFields();
T handle(T target);
}
以下是字段表示。
public interface Field<T> {
void setValue(String value);
T getField();
String getFieldName();
}
现在我需要解析json模式并返回一个处理程序列表。以下是我的尝试。
private List<Handler> parseJsonSchema(Handler handler, String jsonSchema) throws JSONMapperException {
List<Handler> handlerList = new ArrayList<>();
boolean isParentLeaf = false;
Field<ObjectNode> objectNodeField = null;
try {
JsonNode rootNode = objectMapper.readTree(jsonSchema);
Iterator<Map.Entry<String, JsonNode>> childrenIterator = rootNode.fields();
while (childrenIterator.hasNext()) {
Map.Entry<String, JsonNode> field = childrenIterator.next();
System.out.println("Key: " + field.getKey() + "\tValue:" + field.getValue());
if (field.getValue().has("attributeValue") && field.getValue().has("attributeType")
&& field.getValue().has("dataType")) {
if (handler == null) {
handler = jsonNodeHandlerFactory.create(field.getKey());
isParentLeaf = true;
}
JsonNode valueNode = field.getValue();
//String fieldName = valueNode.get("attributeValue").toString();
String fieldName = field.getKey();
String dataType = valueNode.get("dataType").toString();
switch (dataType) {
case "1":
objectNodeField = dataFieldFactory.createStringField(fieldName);
break;
case "2":
objectNodeField = dataFieldFactory.createIntField(fieldName);
break;
case "3":
objectNodeField = dataFieldFactory.createBooleanField(fieldName);
break;
default:
break;
}
handler.addField(objectNodeField);
if(isParentLeaf) {
handlerList.add(handler);
handler =null;
}
} else {
handler = jsonNodeHandlerFactory.create(field.getKey());
List<Handler> handlers = parseJsonSchema(handler, field.getValue().toString());
for (Handler handler1 : handlers) {
if(handler != null) { //means we already have a handler and we've come into another handler
handler.addHandler(handler1);
handlerList.add(handler);
} else {
handlerList.add(handler1);
}
}
handler = null;
}
if ((handler != null && handler.getFields().size() == rootNode.size())) {
handlerList.add(handler);
}
}
} catch (IOException e) {
logger.error(JSON_SCHEMA_PARSE_EXCEPTION, e);
throw new JSONMapperException(JSON_SCHEMA_PARSE_EXCEPTION);
}
return handlerList;
}
但是当处理程序中有处理程序时,它会覆盖处理程序。它也看起来非常笨拙,有太多的空分配和检查。有没有更好的方法来做这个方法已经做的事情?这将返回架构作为处理程序列表。任何帮助将不胜感激。
答案 0 :(得分:1)
首先,在我看来,这个问题的一个更好的地方是Code Review论坛。
但是,为了帮助您入门,我提供了一些提示:
正如你的直觉告诉你的那样,代码确实看起来很笨拙&#34;或者换句话说,没有在很大程度上实施OO原则(甚至只是简单的SW开发原则)。
我首先看一下if statememnt的长分支,使每个分支成为单独的方法。像parseField()
和parseHandler()
之类的东西,也许还有其他这样的代码块可以被分离成单独的方法。这将使parseJsonSchema()
方法更具可读性和可调试性(例如,这只是简单的SW开发原则)
上述原则不仅适用于长代码块,也适用于可以隔离的短代码,为了清晰起见隐藏细节:if条件决定field
是否为Field
Handler
或isField(Map.Entry<String, JsonNode> entry)
。将其变为field
方法。也许您现在可以看到为什么命名Field
不是最佳选择,如果它可以是Handler
或handler1
。 parseJsonSchema()
变量的注释相同。它必须有一个更易于理解的名称。
很高兴你使用工厂来实例化Fields。然而,除了调用构造函数进行concreate Field实现之外,我没有看到你的工厂还在做什么,因为json的所有解析和决策都是在Field
中完成的。根据经验,工厂应该为每种返回类型(没有泛型)有一种方法。因此,您的工厂正在返回各种createField()
,然后它应该只有JsonNode
得到objectNodeField
并解析它需要决定实例化哪个实现。
要遵循的另一个OO原则:尽可能在最内层范围内声明您的变量。 parseJsonSchema()
只能在处理字段的分支内访问,因此请将声明放在此分支的大括号内。不要在整个parseField()
的范围内声明它。这使得更容易理解变量的范围并避免在适用范围之外的错误访问。
现在,如果将分支转换为objectNodeField
方法,则ObjectNode
变量将成为该新方法的本地变量。了解它如何使代码在清晰度和稳定性方面更好?
在我们查看这个变量的时候,我在想为什么在它的具体差异为String,Integer和Boolean时,为什么要使用它的泛型StringField
?
如何handle()
实现Field
接口的ObjectNode
方法并实际处理objectNodeField
? IMO,Field<Object>
应该被声明为Field<?>
或更好,Jackson
因此它可以托管上述类型。
很高兴您使用递归来解析处理程序和字段的层次结构。但是,您真的认为每次解析Json String,然后将树重新打包回String以调用下一个递归是最好的方法吗?
我可以继续但不想让你不堪重负。最后请注意,我认为您应该熟悉将Map
解析为多层次结构 <web-app ...>
<session-config>
<session-timeout>20</session-timeout>
</session-config>
</web-app>
的Json字符串的功能。我认为遍历Json Nodes更容易。