基础知识漫谈(4):讲讲元数据
说几个风马牛不相及的词儿,spring的依赖注入定义,hibernate的数据映射定义,XML的DTD,再就是我们常说的报文格式。
如果对它们不甚了解,请参考章节一《想到哪儿写到哪儿》。有了基本的了解之后,应当隐约之中有一种感觉,“它们很相似”。
本篇文章要说的就是这个相似性,我管它叫做数据格式\元数据,DataSchema\MetaData。当然,元数据的定义是要大于数据格式的,本文将它们当成同一个概念。
什么是数据格式?看看这段XML(1):
1 2 3 4 5 6 7 8 9 10 11 |
|
直觉上,我们可以把它可以这个类联系起来:
-
class User{
-
-
String name=”Abby”;
-
-
int id=1;
-
-
}
对于该User类的实例(User u1=new User()),该XML和该Java类定义,都是用于描述u1这个类实例的元数据。
再深化一点儿,看看如下这段XML(2):
-
<Element name=”Bean” min=”0” max=”*”>
-
-
<Attribute name=”type” type=”String”/>
-
-
<Element name=”fields” min=”1” max=”1” >
-
-
<Attribute name=”type” type=”String”/>
-
-
<Attribute name=”name” type=”String”/>
-
-
<Attribute name=”default” type=”Object”/>
-
-
</Element>
-
-
</Element>
能不能得出结论:XML(2)是XML(1)的元数据?
如果你还是学生,相信你一定想起了教科书上的一句话:元数据的元数据是元元数据。
总结一下刚才所说的:
1、元数据本身可以是任何描述性质的结构,比如XML、JAVA类定义、JSON,等等。
2、只要是按照特定规范组合起来的数据,一定具备特定的数据格式。
3、元数据是个相对的概念,提到它就一定要说明,该元数据是什么玩意儿的元数据。就像上面的XML(1),它和User实例对比的时候,是元数据,和XML(2)对比的时候,又是被描述实体。
4、同一种数据实体,可以用不同的元数据结构(XML,JAVA)来描述。
现在再回头看看章节头的内容,是不是清晰很多了?
依赖注入和数据映射定义都是框架用于描述JavaBean的,DTD是用来描述XML的,报文格式是用来描述报文的。
它们在相对意义上,都是数据格式。
利用对这个概念的理解,我们可以做到“自动解析指定格式的A实例为B实例”。
这里的A、B可以用XML、Json、Java等等替代。这里以XML转化为Java实例作为示例讲解。
步骤一:
分析Java实例元元数据,得出Java实体类定义结构如下:
类名,包含多个字段
类字段,包含字段名,字段类型,默认值
字段类型,有很多划分方式,这里划分为基本数据类型(含String),集合类型(List、Map或者数组),引用类型(另外一个自定义格式)。
如何用XML来描述它们?回头看看XML(1)的结构你就懂。
除了XML描述文件,我们还需要一个模型来归纳它们,见接口IDataSchema。
-
package galaxy.ide.configurable.editor.schema;
-
-
import galaxy.ide.configurable.editor.persistent.IPersistentHelper;
-
-
import java.util.Map.Entry;
-
import java.util.Set;
-
-
/**
-
* 数据结构接口,使用 {@link DataSchemaAnalysts}可以根据数据结构来获取具体数据模型中的具体值
-
*
-
* @author caiyu
-
* @date 2014-1-13
-
*/
-
@SuppressWarnings("rawtypes")
-
public interface IDataSchema<T> {
-
/**
-
* 获取Class
-
*
-
* @return
-
*/
-
Class<T> getOwner();
-
-
void setOwner(Class<T> type);
-
-
/**
-
* 数据结构关键字
-
*
-
* @return
-
*/
-
String getId();
-
-
/**
-
* 添加字段
-
*
-
* @param memberId
-
* @param member
-
*/
-
void addField(String memberId, IDataSchema<?> member);
-
-
/**
-
* 获取字段
-
*
-
* @param memberId
-
* @return
-
*/
-
IDataSchema<?> getField(String memberId);
-
-
/**
-
* 获取全部的字段
-
*
-
* @return
-
*/
-
Set<Entry<String, IDataSchema<?>>> getFieldEntrySet();
-
-
void setName(String name);
-
-
/**
-
* 获取名称
-
*
-
* @return
-
*/
-
String getName();
-
-
/**
-
* 复制目标结构
-
*
-
* @param schema
-
*/
-
void copy(IDataSchema<?> schema);
-
-
/**
-
* 根据过滤器复制
-
*
-
* @param schema
-
* @param filters
-
*/
-
void copy(IDataSchema<?> schema, String... filters);
-
-
/**
-
*
-
* @return
-
*/
-
String getBundleId();
-
-
/**
-
* 设置bundleId
-
*
-
* @param bundleId
-
* @return
-
*/
-
void setBundleId(String bundleId);
-
-
/**
-
* 获取Class分类
-
*
-
* @return
-
*/
-
ClassType getType();
-
-
/**
-
* 获取当前结构默认的持久化助手
-
*
-
* @return
-
*/
-
IPersistentHelper getDefaultPersistentHelper();
-
-
void setDefualtPersistentHelper(IPersistentHelper persistHelper);
-
-
IDataSchema<?> getFirstField();
-
-
boolean containsField(String key);
-
-
void addProperty(String key, Object property);
-
-
/**
-
* 获取额外属性
-
*
-
* @param key
-
* @return
-
*/
-
Object getProperty(String key);
-
-
String[] getPropertyKeys();
-
}
步骤二:
有了结构说明,还需要一个Xml解析工具,一个结构分析工具。
Xml解析可以使用dom4j,结构分析工具要自己写,见接口IPersistentHelper。
-
package galaxy.ide.configurable.editor.persistent;
-
-
import galaxy.ide.configurable.editor.schema.IDataSchema;
-
-
/**
-
* 持久化助手接口
-
* <P>
-
* C->Content object class</br> P->Persistent object class
-
*
-
*
-
* @author caiyu
-
* @date 2013-12-19
-
*/
-
public interface IPersistentHelper<C, P> {
-
-
/**
-
* 根据数据结构,从持久化对象中加载内容
-
*
-
* @return
-
*/
-
C load(P persistentTarget, IDataSchema<C> schema);
-
-
/**
-
* 根据数据结构,将内容保存为持久化对象
-
*
-
* @param content
-
*/
-
P save(C content, IDataSchema<C> schema);
-
-
}
其中有三个对象,C和P是可以互相转化的对象,这里分别指Java和Xml,schema是dom4j读取后对象化的结构描述。
参考实现如XmlPersistentHelper(部分源码)所示。
-
/**
-
* 留待日后重写,使用DataSchemaAnalysts分析
-
*
-
* @author caiyu
-
* @date 2014-1-7
-
*/
-
public final class XmlPersistentHelper extends
-
AbstractPersistentHelper<Object, Element> {
-
@SuppressWarnings("rawtypes")
-
@Override
-
public Object load(Element persistentTarget, IDataSchema schema) {
-
// TODO load
-
Object instance = deserialCustomType(persistentTarget, schema);
-
if (instance != null)
-
return instance;
-
-
if (instance == null)
-
instance = deserialBasicType(persistentTarget, schema);
-
if (instance == null)
-
instance = deserialKeyValueType(persistentTarget, schema);
-
if (instance == null)
-
instance = deserialBeanType(persistentTarget, schema);
-
return instance;
-
}
-
-
-
@SuppressWarnings("rawtypes")
-
@Override
-
public Element save(Object content, IDataSchema schema) {
-
Assert.isNotNull(schema);
-
// TODO save
-
Element root = serialCustomType(content, schema);
-
if (content != null) {
-
if (root == null)
-
root = serialBasicType(content, schema);
-
if (root == null)
-
root = serialKeyValueType(content, schema);
-
if (root == null)
-
root = serialBeanType(content, schema);
-
}
-
return root;
-
}
-
-
-
-
public Element serialBeanType(Object content, IDataSchema<?> schema) {
-
Assert.isNotNull(schema);
-
Element root = DocumentFactory.getInstance().createElement(
-
schema.getName());
-
try {
-
Element child = null;
-
for (Entry<String, IDataSchema<?>> field : schema
-
.getFieldEntrySet()) {
-
Field f = DataSchemaAnalysts.getFieldByName(schema.getOwner(),
-
field.getKey());
-
// schema.getOwner().getDeclaredField(field.getKey());
-
f.setAccessible(true);
-
IDataSchema<?> subSchema = field.getValue();
-
Object o = f.get(content);
-
child = save(o, subSchema);
-
if (child != null)
-
root.add(child);
-
f.setAccessible(false);
-
}
-
return root;
-
} catch (IllegalArgumentException e) {
-
e.printStackTrace();
-
} catch (IllegalAccessException e) {
-
e.printStackTrace();
-
} catch (NoSuchFieldException e) {
-
e.printStackTrace();
-
throw new InvalidBeanSchemaException("can not handle schema "
-
+ e.getMessage());
-
}
-
return null;
-
}
-
-
@SuppressWarnings("rawtypes")
-
private Element serialBasicType(Object content, IDataSchema<?> schema) {
-
Element root = DocumentFactory.getInstance().createElement(
-
schema.getName());
-
Class<?> owner = schema.getOwner();
-
boolean flag = false;
-
-
if (owner == String.class || owner == Double.class
-
|| owner == Long.class || owner == Float.class
-
|| owner == Integer.class || owner == Character.class
-
|| owner == Boolean.class) {
-
flag = true;
-
root.setText(content.toString());
-
} else {
-
if (List.class.isAssignableFrom(owner)) {
-
Assert.isTrue(content instanceof List,
-
"unaccept content owner: " + content.getClass());
-
List list = (List) content;
-
seriaList(list, schema, root);
-
flag = true;
-
} else if (Map.class.isAssignableFrom(owner)) {
-
Assert.isTrue(content instanceof Map,
-
"unaccept content owner: " + content.getClass());
-
Map map = (Map) content;
-
serialMap(map, schema, root);
-
flag = true;
-
}
-
}
-
if (flag)
-
return root;
-
-
return null;
-
}
-
-
@SuppressWarnings("rawtypes")
-
private void serialMap(Map map, IDataSchema<?> schema, Element root) {
-
String primaryKey = (String) schema
-
.getProperty(ExtensionConstants.PRIMARY_KEY);
-
if (primaryKey == null || primaryKey.trim().length() == 0) {
-
Element child = null;
-
Set<Entry<String, IDataSchema<?>>> set = schema.getFieldEntrySet();
-
for (Entry<String, IDataSchema<?>> entry : set) {
-
Object o = map.get(entry.getValue().getName());
-
child = save(o, entry.getValue());
-
if (child != null)
-
root.add(child);
-
}
-
} else {
-
IDataSchema<?> valueSchema = schema.getFieldEntrySet().iterator()
-
.next().getValue();
-
Element child = null;
-
for (Object value : map.values()) {
-
child = save(value, valueSchema);
-
if (child != null)
-
root.add(child);
-
}
-
}
-
}
-
-
-
/**
-
* 反序列化基本数据类型,比如String,Integer,Double等
-
*
-
* @param schema
-
* @param element
-
* @return
-
*/
-
private Object deserialBasicType(Element persistentTarget,
-
IDataSchema<?> schema) {
-
Class<?> owner = schema.getOwner();
-
String value = persistentTarget == null ? null : persistentTarget
-
.getText();
-
ClassType type = schema.getType();
-
switch (type) {
-
case Map:
-
case List:
-
if (value == null)
-
break;
-
try {
-
// 处理集合型数据
-
if (List.class.isAssignableFrom(owner)) {
-
Object o = schema.getOwner().newInstance();
-
Assert.isTrue(o instanceof List, "unaccept content owner: "
-
+ o.getClass());
-
return deserialList(persistentTarget, schema, o);
-
} else if (Map.class.isAssignableFrom(owner)) {
-
Object o = schema.getOwner().newInstance();
-
Assert.isTrue(o instanceof Map, "unaccept content owner: "
-
+ o.getClass());
-
return deserialMap(persistentTarget, schema, o);
-
}
-
} catch (InstantiationException e1) {
-
e1.printStackTrace();
-
} catch (IllegalAccessException e1) {
-
e1.printStackTrace();
-
}
-
break;
-
default:
-
return type.handle(value);
-
}
-
return null;
-
}
-
-
}
步骤三:
要实现为一个JavaBean存取值,还需要提供一个数据分析工具,利用反射,从JavaBean里抽取指定值,或者设置值。参见DataSchemaAnalysts。
-
/**
-
*
-
* 数据结构解析器
-
*
-
* @author caiyu
-
* @date 2014-4-15
-
*/
-
public class DataSchemaAnalysts {
-
public static Field getFieldByName(Object obj, String fieldName)
-
throws NoSuchFieldException {
-
Assert.isNotNull(obj, fieldName + " should not be null");
-
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass
-
.getSuperclass()) {
-
try {
-
return superClass.getDeclaredField(fieldName);
-
} catch (Exception e) {
-
// System.out.println(e.getLocalizedMessage());
-
}
-
}
-
throw new NoSuchFieldException("can not find field: [" + fieldName
-
+ "] in " + obj);
-
}
-
-
public static Field getFieldByName(Class<?> clazz, String fieldName)
-
throws NoSuchFieldException {
-
for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass
-
.getSuperclass()) {
-
try {
-
return superClass.getDeclaredField(fieldName);
-
} catch (NoSuchFieldException e) {
-
// System.out.println(e.getLocalizedMessage());
-
}
-
}
-
throw new NoSuchFieldException(clazz + " " + fieldName);
-
}
-
-
public static Field[] getAllFields(Class<?> clazz) {
-
List<Field> field = new ArrayList<Field>(20);
-
for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass
-
.getSuperclass()) {
-
field.addAll(Arrays.asList(superClass.getDeclaredFields()));
-
}
-
return field.toArray(new Field[0]);
-
}
-
-
/**
-
* 根据父对象(parent)的数据结构(parentSchema),分析抽取关键字(key)对应的子对象
-
*
-
* @param parentSchema
-
* @param parent
-
* @param key
-
* @return
-
* @throws NoSuchFieldException
-
* @throws SecurityException
-
* @throws IllegalArgumentException
-
* @throws IllegalAccessException
-
* @throws InvocationTargetException
-
*/
-
@SuppressWarnings("rawtypes")
-
public static Object analyse(IDataSchema<?> parentSchema, Object parent,
-
String key) throws NoSuchFieldException, SecurityException,
-
IllegalArgumentException, IllegalAccessException,
-
InvocationTargetException {
-
ClassType type = parentSchema.getType();
-
if (type == null)
-
throw new AnalyseDataSchemaException("type of "
-
+ parentSchema.getName() + " [" + parent
-
+ "] can not be null");
-
switch (type) {
-
case Bean:
-
Field f = getFieldByName(parent, key);
-
f.setAccessible(true);
-
Object o = f.get(parent);
-
f.setAccessible(false);
-
return o;
-
case Map:
-
return ((Map) parent).get(key);
-
case List:
-
int i = Integer.parseInt(key);
-
return ((List) parent).get(i);
-
case Key_Value:
-
DataType dataType = parentSchema.getOwner().getAnnotation(
-
DataType.class);
-
if (dataType != null && dataType.value() == DataTypeValue.MAP) {
-
Method getMethod = extraMethodByAnnotation(
-
parentSchema.getOwner(), get.class);
-
return getMethod.invoke(parent, key);
-
}
-
}
-
return null;
-
}
-
-
/**
-
* 抽取含有指定注解的方法
-
*
-
* @param owner
-
* @param annotationClass
-
* @return
-
*/
-
public static Method extraMethodByAnnotation(Class<?> owner,
-
Class<? extends Annotation> annotationClass) {
-
for (Method method : owner.getDeclaredMethods()) {
-
Annotation t = method.getAnnotation(annotationClass);
-
if (t != null) {
-
return method;
-
}
-
-
}
-
throw new InvalidAnnotationConfigException(owner + " has no annoation "
-
+ annotationClass);
-
}
-
-
public static ClassType analyseClassType(final Class<?> owner) {
-
if (owner == String.class)
-
return ClassType.String;
-
else if (owner == Integer.class)
-
return ClassType.Integer;
-
else if (owner == Double.class)
-
return ClassType.Double;
-
else if (owner == Float.class)
-
return ClassType.Float;
-
else if (owner == Long.class)
-
return ClassType.Long;
-
else if (owner == Character.class)
-
return ClassType.Character;
-
else if (owner == Boolean.class)
-
return ClassType.Boolean;
-
else if (List.class.isAssignableFrom(owner))
-
return ClassType.List;
-
else if (Map.class.isAssignableFrom(owner))
-
return ClassType.Map;
-
else if (owner.getAnnotation(DataType.class) != null) {
-
if (owner.getAnnotation(DataType.class).value() == DataTypeValue.MAP) {
-
return ClassType.Key_Value;
-
}
-
}
-
return ClassType.Bean;
-
}
-
-
/**
-
* 将value应用到指定的target上
-
*
-
* @param targetSchema
-
* @param target
-
* @param valueSchema
-
* @param value
-
* @throws SecurityException
-
* @throws NoSuchFieldException
-
* @throws IllegalAccessException
-
* @throws IllegalArgumentException
-
* @throws InvocationTargetException
-
*/
-
@SuppressWarnings({ "unchecked", "rawtypes" })
-
public static void perform(IDataSchema<?> targetSchema, Object target,
-
String key, Object value) throws NoSuchFieldException,
-
SecurityException, IllegalArgumentException,
-
IllegalAccessException, InvocationTargetException {
-
// TODO Auto-generated method stub
-
-
ClassType type = targetSchema.getType();
-
if (type == null)
-
throw new AnalyseDataSchemaException("type of "
-
+ targetSchema.getName() + " [" + target
-
+ "] can not be null");
-
switch (type) {
-
case Bean:
-
Field f = getFieldByName(target, key);
-
f.setAccessible(true);
-
if (f.getType().isPrimitive() && value == null)
-
value = DataSchemaUtil.getPrimitiveDefaultValue(f.getType());
-
f.set(target, value);
-
f.setAccessible(false);
-
break;
-
case Map:
-
String primaryKey = (String) targetSchema
-
.getProperty(ExtensionConstants.PRIMARY_KEY);
-
if (primaryKey != null && primaryKey.length() != 0) {
-
IDataSchema<?> childSchema = targetSchema.getFieldEntrySet()
-
.iterator().next().getValue();
-
Object k = analyse(childSchema, value, (String) primaryKey);
-
((Map) target).put(k, value);
-
} else
-
((Map) target).put(key, value);
-
break;
-
case List:
-
if (target != null)
-
((List) target).add(value);
-
else
-
throw new IllegalArgumentException(
-
"XML format error, missing element "
-
+ targetSchema.getName());
-
break;
-
case Key_Value:
-
DataType dataType = targetSchema.getOwner().getAnnotation(
-
DataType.class);
-
if (dataType != null && dataType.value() == DataTypeValue.MAP) {
-
Method putMethod = extraMethodByAnnotation(
-
targetSchema.getOwner(), put.class);
-
putMethod.invoke(target, key, value);
-
}
-
break;
-
}
-
}
-
}
扩展思考:完全把Java映射成XML是可能的,因为复杂是由简单构成,虽然Java里有大量的类型,但一直跟进到代码深处你会发现,所有的复杂的类结构,最终都是基础数据类型。可是,完全把Java映射成XML是不可行的,因为这棵定义树不知道到底有多深,有可能不是人工能完成的工作量,那么,想想,如何做到呢?
最近的三篇都应用到了面向对象相关基础来分析复杂的需求,下一篇文章将专门讲解什么叫万物皆对象,并应用来解析SQL语言。
特此声明:因此文章个人认为总结的很到位,很有价值,特此转载!转载地址:http://www.cnblogs.com/anrainie/p/5622533.html
文章来源: blog.csdn.net,作者:LookForDream_,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/LookForDream_/article/details/83536916
- 点赞
- 收藏
- 关注作者
评论(0)