基础知识漫谈(4):讲讲元数据

举报
轻狂书生FS 发表于 2020/11/29 00:58:07 2020/11/29
【摘要】 说几个风马牛不相及的词儿,spring的依赖注入定义,hibernate的数据映射定义,XML的DTD,再就是我们常说的报文格式。 如果对它们不甚了解,请参考章节一《想到哪儿写到哪儿》。有了基本的了解之后,应当隐约之中有一种感觉,“它们很相似”。 本篇文章要说的就是这个相似性,我管它叫做数据格式\元数据,DataSchema\MetaData。当然,元数据的定义是要大于数...

说几个风马牛不相及的词儿,spring的依赖注入定义,hibernate的数据映射定义,XML的DTD,再就是我们常说的报文格式

如果对它们不甚了解,请参考章节一《想到哪儿写到哪儿》。有了基本的了解之后,应当隐约之中有一种感觉,“它们很相似”。

本篇文章要说的就是这个相似性,我管它叫做数据格式\元数据,DataSchema\MetaData。当然,元数据的定义是要大于数据格式的,本文将它们当成同一个概念。

什么是数据格式?看看这段XML(1):

1

2

3

4

5

6

7

8

9

10

11

<Bean type=”com.nlo.pojo.User”>

 

    <Fields>

 

        <Bean name=”name” type=”java.lang.String” default=”Abby”/>

 

        <Bean name=”id” type=”int” default=”1”/>

 

    </Fields>

 

</Bean>

 

直觉上,我们可以把它可以这个类联系起来:

 


  
  1. class User{
  2. String name=”Abby”;
  3. int id=1;
  4. }

 

 

对于该User类的实例(User u1=new User()),该XML和该Java类定义,都是用于描述u1这个类实例的元数据。

再深化一点儿,看看如下这段XML(2):

 


  
  1. <Element name=”Bean” min=”0” max=”*”>
  2. <Attribute name=”type” type=”String”/>
  3. <Element name=”fields” min=”1” max=”1” >
  4. <Attribute name=”type” type=”String”/>
  5. <Attribute name=”name” type=”String”/>
  6. <Attribute name=”default” type=”Object”/>
  7. </Element>
  8. </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。

 

 


  
  1. package galaxy.ide.configurable.editor.schema;
  2. import galaxy.ide.configurable.editor.persistent.IPersistentHelper;
  3. import java.util.Map.Entry;
  4. import java.util.Set;
  5. /**
  6. * 数据结构接口,使用 {@link DataSchemaAnalysts}可以根据数据结构来获取具体数据模型中的具体值
  7. *
  8. * @author caiyu
  9. * @date 2014-1-13
  10. */
  11. @SuppressWarnings("rawtypes")
  12. public interface IDataSchema<T> {
  13. /**
  14. * 获取Class
  15. *
  16. * @return
  17. */
  18. Class<T> getOwner();
  19. void setOwner(Class<T> type);
  20. /**
  21. * 数据结构关键字
  22. *
  23. * @return
  24. */
  25. String getId();
  26. /**
  27. * 添加字段
  28. *
  29. * @param memberId
  30. * @param member
  31. */
  32. void addField(String memberId, IDataSchema<?> member);
  33. /**
  34. * 获取字段
  35. *
  36. * @param memberId
  37. * @return
  38. */
  39. IDataSchema<?> getField(String memberId);
  40. /**
  41. * 获取全部的字段
  42. *
  43. * @return
  44. */
  45. Set<Entry<String, IDataSchema<?>>> getFieldEntrySet();
  46. void setName(String name);
  47. /**
  48. * 获取名称
  49. *
  50. * @return
  51. */
  52. String getName();
  53. /**
  54. * 复制目标结构
  55. *
  56. * @param schema
  57. */
  58. void copy(IDataSchema<?> schema);
  59. /**
  60. * 根据过滤器复制
  61. *
  62. * @param schema
  63. * @param filters
  64. */
  65. void copy(IDataSchema<?> schema, String... filters);
  66. /**
  67. *
  68. * @return
  69. */
  70. String getBundleId();
  71. /**
  72. * 设置bundleId
  73. *
  74. * @param bundleId
  75. * @return
  76. */
  77. void setBundleId(String bundleId);
  78. /**
  79. * 获取Class分类
  80. *
  81. * @return
  82. */
  83. ClassType getType();
  84. /**
  85. * 获取当前结构默认的持久化助手
  86. *
  87. * @return
  88. */
  89. IPersistentHelper getDefaultPersistentHelper();
  90. void setDefualtPersistentHelper(IPersistentHelper persistHelper);
  91. IDataSchema<?> getFirstField();
  92. boolean containsField(String key);
  93. void addProperty(String key, Object property);
  94. /**
  95. * 获取额外属性
  96. *
  97. * @param key
  98. * @return
  99. */
  100. Object getProperty(String key);
  101. String[] getPropertyKeys();
  102. }

 

 

步骤二:

有了结构说明,还需要一个Xml解析工具,一个结构分析工具。

Xml解析可以使用dom4j,结构分析工具要自己写,见接口IPersistentHelper。

 

 


  
  1. package galaxy.ide.configurable.editor.persistent;
  2. import galaxy.ide.configurable.editor.schema.IDataSchema;
  3. /**
  4. * 持久化助手接口
  5. * <P>
  6. * C->Content object class</br> P->Persistent object class
  7. *
  8. *
  9. * @author caiyu
  10. * @date 2013-12-19
  11. */
  12. public interface IPersistentHelper<C, P> {
  13. /**
  14. * 根据数据结构,从持久化对象中加载内容
  15. *
  16. * @return
  17. */
  18. C load(P persistentTarget, IDataSchema<C> schema);
  19. /**
  20. * 根据数据结构,将内容保存为持久化对象
  21. *
  22. * @param content
  23. */
  24. P save(C content, IDataSchema<C> schema);
  25. }

 

 

其中有三个对象,C和P是可以互相转化的对象,这里分别指Java和Xml,schema是dom4j读取后对象化的结构描述。

参考实现如XmlPersistentHelper(部分源码)所示。

 

 


  
  1. /**
  2. * 留待日后重写,使用DataSchemaAnalysts分析
  3. *
  4. * @author caiyu
  5. * @date 2014-1-7
  6. */
  7. public final class XmlPersistentHelper extends
  8. AbstractPersistentHelper<Object, Element> {
  9. @SuppressWarnings("rawtypes")
  10. @Override
  11. public Object load(Element persistentTarget, IDataSchema schema) {
  12. // TODO load
  13. Object instance = deserialCustomType(persistentTarget, schema);
  14. if (instance != null)
  15. return instance;
  16. if (instance == null)
  17. instance = deserialBasicType(persistentTarget, schema);
  18. if (instance == null)
  19. instance = deserialKeyValueType(persistentTarget, schema);
  20. if (instance == null)
  21. instance = deserialBeanType(persistentTarget, schema);
  22. return instance;
  23. }
  24. @SuppressWarnings("rawtypes")
  25. @Override
  26. public Element save(Object content, IDataSchema schema) {
  27. Assert.isNotNull(schema);
  28. // TODO save
  29. Element root = serialCustomType(content, schema);
  30. if (content != null) {
  31. if (root == null)
  32. root = serialBasicType(content, schema);
  33. if (root == null)
  34. root = serialKeyValueType(content, schema);
  35. if (root == null)
  36. root = serialBeanType(content, schema);
  37. }
  38. return root;
  39. }
  40. public Element serialBeanType(Object content, IDataSchema<?> schema) {
  41. Assert.isNotNull(schema);
  42. Element root = DocumentFactory.getInstance().createElement(
  43. schema.getName());
  44. try {
  45. Element child = null;
  46. for (Entry<String, IDataSchema<?>> field : schema
  47. .getFieldEntrySet()) {
  48. Field f = DataSchemaAnalysts.getFieldByName(schema.getOwner(),
  49. field.getKey());
  50. // schema.getOwner().getDeclaredField(field.getKey());
  51. f.setAccessible(true);
  52. IDataSchema<?> subSchema = field.getValue();
  53. Object o = f.get(content);
  54. child = save(o, subSchema);
  55. if (child != null)
  56. root.add(child);
  57. f.setAccessible(false);
  58. }
  59. return root;
  60. } catch (IllegalArgumentException e) {
  61. e.printStackTrace();
  62. } catch (IllegalAccessException e) {
  63. e.printStackTrace();
  64. } catch (NoSuchFieldException e) {
  65. e.printStackTrace();
  66. throw new InvalidBeanSchemaException("can not handle schema "
  67. + e.getMessage());
  68. }
  69. return null;
  70. }
  71. @SuppressWarnings("rawtypes")
  72. private Element serialBasicType(Object content, IDataSchema<?> schema) {
  73. Element root = DocumentFactory.getInstance().createElement(
  74. schema.getName());
  75. Class<?> owner = schema.getOwner();
  76. boolean flag = false;
  77. if (owner == String.class || owner == Double.class
  78. || owner == Long.class || owner == Float.class
  79. || owner == Integer.class || owner == Character.class
  80. || owner == Boolean.class) {
  81. flag = true;
  82. root.setText(content.toString());
  83. } else {
  84. if (List.class.isAssignableFrom(owner)) {
  85. Assert.isTrue(content instanceof List,
  86. "unaccept content owner: " + content.getClass());
  87. List list = (List) content;
  88. seriaList(list, schema, root);
  89. flag = true;
  90. } else if (Map.class.isAssignableFrom(owner)) {
  91. Assert.isTrue(content instanceof Map,
  92. "unaccept content owner: " + content.getClass());
  93. Map map = (Map) content;
  94. serialMap(map, schema, root);
  95. flag = true;
  96. }
  97. }
  98. if (flag)
  99. return root;
  100. return null;
  101. }
  102. @SuppressWarnings("rawtypes")
  103. private void serialMap(Map map, IDataSchema<?> schema, Element root) {
  104. String primaryKey = (String) schema
  105. .getProperty(ExtensionConstants.PRIMARY_KEY);
  106. if (primaryKey == null || primaryKey.trim().length() == 0) {
  107. Element child = null;
  108. Set<Entry<String, IDataSchema<?>>> set = schema.getFieldEntrySet();
  109. for (Entry<String, IDataSchema<?>> entry : set) {
  110. Object o = map.get(entry.getValue().getName());
  111. child = save(o, entry.getValue());
  112. if (child != null)
  113. root.add(child);
  114. }
  115. } else {
  116. IDataSchema<?> valueSchema = schema.getFieldEntrySet().iterator()
  117. .next().getValue();
  118. Element child = null;
  119. for (Object value : map.values()) {
  120. child = save(value, valueSchema);
  121. if (child != null)
  122. root.add(child);
  123. }
  124. }
  125. }
  126. /**
  127. * 反序列化基本数据类型,比如String,Integer,Double等
  128. *
  129. * @param schema
  130. * @param element
  131. * @return
  132. */
  133. private Object deserialBasicType(Element persistentTarget,
  134. IDataSchema<?> schema) {
  135. Class<?> owner = schema.getOwner();
  136. String value = persistentTarget == null ? null : persistentTarget
  137. .getText();
  138. ClassType type = schema.getType();
  139. switch (type) {
  140. case Map:
  141. case List:
  142. if (value == null)
  143. break;
  144. try {
  145. // 处理集合型数据
  146. if (List.class.isAssignableFrom(owner)) {
  147. Object o = schema.getOwner().newInstance();
  148. Assert.isTrue(o instanceof List, "unaccept content owner: "
  149. + o.getClass());
  150. return deserialList(persistentTarget, schema, o);
  151. } else if (Map.class.isAssignableFrom(owner)) {
  152. Object o = schema.getOwner().newInstance();
  153. Assert.isTrue(o instanceof Map, "unaccept content owner: "
  154. + o.getClass());
  155. return deserialMap(persistentTarget, schema, o);
  156. }
  157. } catch (InstantiationException e1) {
  158. e1.printStackTrace();
  159. } catch (IllegalAccessException e1) {
  160. e1.printStackTrace();
  161. }
  162. break;
  163. default:
  164. return type.handle(value);
  165. }
  166. return null;
  167. }
  168. }

 

 

步骤三:

要实现为一个JavaBean存取值,还需要提供一个数据分析工具,利用反射,从JavaBean里抽取指定值,或者设置值。参见DataSchemaAnalysts。

 

 


  
  1. /**
  2. *
  3. * 数据结构解析器
  4. *
  5. * @author caiyu
  6. * @date 2014-4-15
  7. */
  8. public class DataSchemaAnalysts {
  9. public static Field getFieldByName(Object obj, String fieldName)
  10. throws NoSuchFieldException {
  11. Assert.isNotNull(obj, fieldName + " should not be null");
  12. for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass
  13. .getSuperclass()) {
  14. try {
  15. return superClass.getDeclaredField(fieldName);
  16. } catch (Exception e) {
  17. // System.out.println(e.getLocalizedMessage());
  18. }
  19. }
  20. throw new NoSuchFieldException("can not find field: [" + fieldName
  21. + "] in " + obj);
  22. }
  23. public static Field getFieldByName(Class<?> clazz, String fieldName)
  24. throws NoSuchFieldException {
  25. for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass
  26. .getSuperclass()) {
  27. try {
  28. return superClass.getDeclaredField(fieldName);
  29. } catch (NoSuchFieldException e) {
  30. // System.out.println(e.getLocalizedMessage());
  31. }
  32. }
  33. throw new NoSuchFieldException(clazz + " " + fieldName);
  34. }
  35. public static Field[] getAllFields(Class<?> clazz) {
  36. List<Field> field = new ArrayList<Field>(20);
  37. for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass
  38. .getSuperclass()) {
  39. field.addAll(Arrays.asList(superClass.getDeclaredFields()));
  40. }
  41. return field.toArray(new Field[0]);
  42. }
  43. /**
  44. * 根据父对象(parent)的数据结构(parentSchema),分析抽取关键字(key)对应的子对象
  45. *
  46. * @param parentSchema
  47. * @param parent
  48. * @param key
  49. * @return
  50. * @throws NoSuchFieldException
  51. * @throws SecurityException
  52. * @throws IllegalArgumentException
  53. * @throws IllegalAccessException
  54. * @throws InvocationTargetException
  55. */
  56. @SuppressWarnings("rawtypes")
  57. public static Object analyse(IDataSchema<?> parentSchema, Object parent,
  58. String key) throws NoSuchFieldException, SecurityException,
  59. IllegalArgumentException, IllegalAccessException,
  60. InvocationTargetException {
  61. ClassType type = parentSchema.getType();
  62. if (type == null)
  63. throw new AnalyseDataSchemaException("type of "
  64. + parentSchema.getName() + " [" + parent
  65. + "] can not be null");
  66. switch (type) {
  67. case Bean:
  68. Field f = getFieldByName(parent, key);
  69. f.setAccessible(true);
  70. Object o = f.get(parent);
  71. f.setAccessible(false);
  72. return o;
  73. case Map:
  74. return ((Map) parent).get(key);
  75. case List:
  76. int i = Integer.parseInt(key);
  77. return ((List) parent).get(i);
  78. case Key_Value:
  79. DataType dataType = parentSchema.getOwner().getAnnotation(
  80. DataType.class);
  81. if (dataType != null && dataType.value() == DataTypeValue.MAP) {
  82. Method getMethod = extraMethodByAnnotation(
  83. parentSchema.getOwner(), get.class);
  84. return getMethod.invoke(parent, key);
  85. }
  86. }
  87. return null;
  88. }
  89. /**
  90. * 抽取含有指定注解的方法
  91. *
  92. * @param owner
  93. * @param annotationClass
  94. * @return
  95. */
  96. public static Method extraMethodByAnnotation(Class<?> owner,
  97. Class<? extends Annotation> annotationClass) {
  98. for (Method method : owner.getDeclaredMethods()) {
  99. Annotation t = method.getAnnotation(annotationClass);
  100. if (t != null) {
  101. return method;
  102. }
  103. }
  104. throw new InvalidAnnotationConfigException(owner + " has no annoation "
  105. + annotationClass);
  106. }
  107. public static ClassType analyseClassType(final Class<?> owner) {
  108. if (owner == String.class)
  109. return ClassType.String;
  110. else if (owner == Integer.class)
  111. return ClassType.Integer;
  112. else if (owner == Double.class)
  113. return ClassType.Double;
  114. else if (owner == Float.class)
  115. return ClassType.Float;
  116. else if (owner == Long.class)
  117. return ClassType.Long;
  118. else if (owner == Character.class)
  119. return ClassType.Character;
  120. else if (owner == Boolean.class)
  121. return ClassType.Boolean;
  122. else if (List.class.isAssignableFrom(owner))
  123. return ClassType.List;
  124. else if (Map.class.isAssignableFrom(owner))
  125. return ClassType.Map;
  126. else if (owner.getAnnotation(DataType.class) != null) {
  127. if (owner.getAnnotation(DataType.class).value() == DataTypeValue.MAP) {
  128. return ClassType.Key_Value;
  129. }
  130. }
  131. return ClassType.Bean;
  132. }
  133. /**
  134. * 将value应用到指定的target上
  135. *
  136. * @param targetSchema
  137. * @param target
  138. * @param valueSchema
  139. * @param value
  140. * @throws SecurityException
  141. * @throws NoSuchFieldException
  142. * @throws IllegalAccessException
  143. * @throws IllegalArgumentException
  144. * @throws InvocationTargetException
  145. */
  146. @SuppressWarnings({ "unchecked", "rawtypes" })
  147. public static void perform(IDataSchema<?> targetSchema, Object target,
  148. String key, Object value) throws NoSuchFieldException,
  149. SecurityException, IllegalArgumentException,
  150. IllegalAccessException, InvocationTargetException {
  151. // TODO Auto-generated method stub
  152. ClassType type = targetSchema.getType();
  153. if (type == null)
  154. throw new AnalyseDataSchemaException("type of "
  155. + targetSchema.getName() + " [" + target
  156. + "] can not be null");
  157. switch (type) {
  158. case Bean:
  159. Field f = getFieldByName(target, key);
  160. f.setAccessible(true);
  161. if (f.getType().isPrimitive() && value == null)
  162. value = DataSchemaUtil.getPrimitiveDefaultValue(f.getType());
  163. f.set(target, value);
  164. f.setAccessible(false);
  165. break;
  166. case Map:
  167. String primaryKey = (String) targetSchema
  168. .getProperty(ExtensionConstants.PRIMARY_KEY);
  169. if (primaryKey != null && primaryKey.length() != 0) {
  170. IDataSchema<?> childSchema = targetSchema.getFieldEntrySet()
  171. .iterator().next().getValue();
  172. Object k = analyse(childSchema, value, (String) primaryKey);
  173. ((Map) target).put(k, value);
  174. } else
  175. ((Map) target).put(key, value);
  176. break;
  177. case List:
  178. if (target != null)
  179. ((List) target).add(value);
  180. else
  181. throw new IllegalArgumentException(
  182. "XML format error, missing element "
  183. + targetSchema.getName());
  184. break;
  185. case Key_Value:
  186. DataType dataType = targetSchema.getOwner().getAnnotation(
  187. DataType.class);
  188. if (dataType != null && dataType.value() == DataTypeValue.MAP) {
  189. Method putMethod = extraMethodByAnnotation(
  190. targetSchema.getOwner(), put.class);
  191. putMethod.invoke(target, key, value);
  192. }
  193. break;
  194. }
  195. }
  196. }

 

扩展思考:完全把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

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200