概要
在当今数字化时代,数据的处理和分析是企业、科研机构以及各类组织日常运营的核心环节。数据来源广泛,格式多样,常见的数据格式包括XML(可扩展标记语言)和JSON(JavaScript对象表示法)。XML以其结构化和可扩展性强的特点,被广泛应用于配置文件、数据交换以及复杂数据结构的描述;而JSON则因其简洁、易读易解析的特性,在Web开发、API接口以及轻量级数据传输中占据重要地位。然而,在实际的数据处理流程中,我们经常会遇到需要将一种数据格式转换为另一种格式的情况,以满足不同系统、平台或应用之间的数据交互需求。
具体来说,常见的数据映射转换需求包括以下几种场景:
- XML到XML的转换:当数据在不同的XML系统之间传递时,可能需要对数据结构进行调整或优化,以适配目标系统的XML模式。例如,将一个复杂的企业级配置文件从一种XML格式转换为另一种更简洁或更符合规范的XML格式,以提高系统的兼容性和可维护性。
- JSON到JSON的转换:在Web开发和数据处理中,JSON数据结构可能需要根据不同的应用场景进行调整。比如,将一个包含大量嵌套对象和数组的JSON数据重新组织,以便更好地适配前端展示或后端处理逻辑。这种转换可能涉及字段的重命名、数据类型的转换以及数据结构的重组。
- XML到JSON的转换:随着Web技术的发展,越来越多的系统倾向于使用JSON作为数据传输格式。因此,当从一个基于XML的旧系统迁移到一个基于JSON的新系统时,就需要将XML数据转换为JSON格式。例如,将一个XML格式的用户配置文件转换为JSON格式,以便在现代的Web应用中使用。
- JSON到XML的转换:在某些情况下,尽管JSON是主流的数据格式,但某些系统或平台可能仍然依赖XML。例如,将JSON格式的API响应数据转换为XML格式,以适配遗留系统的数据处理需求。
为了高效、准确地完成这些数据格式的转换和映射,我们需要一个灵活且强大的工具或框架。
通过实现这样一个数据格式转换工具,我们不仅可以提高数据处理的效率,还能确保数据在不同系统之间的顺畅交互,从而为企业的数字化转型和数据驱动的决策提供有力支持。
在当前的数据处理领域,虽然有许多数据映射和转换工具可供选择,但很难找到一款完全符合个性化需求的工具。为此,我们开发了一款基于Java的数据转换映射工具,旨在提供一种直观、高效且易于操作的解决方案。该工具的图形界面基于Swing开发,支持XML和JSON两种常见数据格式的转换。
工具功能特点
- 支持多种数据格式
用户可以选择XML和JSON作为源格式和目标格式,工具支持从XML到XML、XML到JSON、JSON到XML以及JSON到JSON的双向转换。 - 树形结构可视化
数据加载后会以树形格式展示,用户可以清晰地查看数据结构。这种可视化方式使得数据映射过程更加直观。 - 拖拽式映射操作
用户可以通过拖动源树的节点到目标树的节点,形成连线完成数据映射。这种拖拽式操作极大地简化了映射过程,降低了操作难度。 - 灵活的映射规则配置
支持基于规则的映射和条件映射,用户可以根据实际需求自定义映射逻辑。
通过Swing开发的图形界面,用户可以轻松上手操作,无需复杂的配置和编程知识。这种无代码或低代码的映射方式,使得工具不仅适用于开发人员,也适合非技术背景的用户。
整体架构流程
经过长时间的钻研分析相关技术,通过实现可视化界面映射,使用Dom4j和fastjson进行xml和json的数据处理,制定了连线的映射规则,形成了一个能够在xml和json之间任意节点进行映射转换的工具。
实现工具的界面如下:
界面上的菜单中能够选择源和目标,将数据加载成树型。
界面上能够拖拽进行连线映射。
可选择菜单中的保存来保存映射连线
可通过菜单中的执行按钮进行数据映射测试
技术细节
-
- 界面实现
界面采用swing开发,不熟悉swing的同学可先学习一下swing的相关知识。代码较多这里只给出部分关键代码。
主题界面代码如下:
/** * * 构造函数 */ public Transformer() { super("数据映射");//标题 instance = this; XMLMapperInit.initLookAndFeel(); setIconImage(XMLMapperInit.loadIcon(XMLMapperInit.ICON_APP).getImage()); mapView = new MapView(this);//映射区 outputXSLT = new JTextArea(); testMessageView = new XmlPane(); testMessageView.setContentType("text/xml"); tabPane = new MyTabbedPane(); getContentPane().add(tabPane); menuBar = new MyMenuBar();//菜单 setJMenuBar(menuBar); } |
这里主要看映射区,在这里创建了左右的两棵树,并生成了中间的连线面板来显示连线。
/** * * 构造函数 * * @param transformer * 界面类 */ public MapView(Transformer transformer) { super(JSplitPane.VERTICAL_SPLIT, true); this.transformer = transformer; srcTree = new MapperTree(false); dstTree = new MapperTree(true); /** * 设置两棵树的ComponentModel属性,树丛该属性中获得消息格式的值 * * @author mzj */ srcTree.setComponentModel(transformer.getSourceModel()); dstTree.setComponentModel(transformer.getDestinationModel()); objectPanel = new ObjectPanel(this, srcTree, dstTree); propView = new PropertyPane(this); leftPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); rightPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); srcTree.addTreeSelectionListener(propView); dstTree.addTreeSelectionListener(propView); leftPane.setBorder(null); rightPane.setBorder(null); this.setBorder(null); // /// // Positioning the main components between the splitters // /// leftPane.setLeftComponent(srcTree.getScrollPane()); leftPane.setRightComponent(rightPane); rightPane.setLeftComponent(objectPanel); rightPane.setRightComponent(dstTree.getScrollPane()); this.setTopComponent(leftPane); // this.setBottomComponent(propView); leftPane.setDividerSize(2); rightPane.setDividerSize(2); addComponentListener(new ComponentListener() { public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentResized(ComponentEvent e) { Dimension d = e.getComponent().getSize(); setSize(d); int x = getWidth() / 3; rightPane.setDividerLocation(x); leftPane.setDividerLocation(x); MapView.this.setDividerLocation((int) ((getHeight() / 3) * 2)); } public void componentShown(ComponentEvent e) { } }); } |
-
- 源和目标的数据加载
通过选择文件菜单中的加载源和加载目标进行数据格式加载。相关代码如下:
public void loadOutFileXML(String dirOne) throws DocumentException { String messageXml = dirOne; if(dirOne.endsWith(".json")) { sourceType=NodeUtil.JSON; }else { sourceType=NodeUtil.XML; } if (messageXml != null && !messageXml.equals("")) { MapperTree.this.load(dirOne); } } public void loadInFileXML(String dirTwo) throws DocumentException { String messageXml = dirTwo; if(dirTwo.endsWith(".json")) { targetType=NodeUtil.JSON; }else { targetType=NodeUtil.XML; }
if (messageXml != null && !messageXml.equals("")) { MapperTree.this.load(dirTwo); } } |
将数据加载成MapperTreeModel加载到界面上显示,MapperTreeModel类继承了DefaultTreeModel。
public class MapperTreeModel extends DefaultTreeModel { private static final long serialVersionUID = 6124199500486839241L; protected boolean bLoaded = false; /** * @author mzj */ private GeneriqueSpoModel componentModel; public MapperTreeModel(MapperTreeNode newRoot) { super(newRoot); } public boolean Load(String filename) { return false; } public boolean isLoaded() { return bLoaded; } /** * @param componentModel */ public void setComponentModel(GeneriqueSpoModel componentModel) { this.componentModel = componentModel; } /** * @return */ public GeneriqueSpoModel getComponentModel() { return componentModel; } } |
-
- 映射界面
这里MapperTree继承了JTree实现了可拖拽的树。ObjectPanel实现了JPanel面板。用于绘制拖拽的连线。ObjectPanel实现了画线的事件。
class MyDragSourceListener implements DragSourceListener { public void dragDropEnd(DragSourceDropEvent event) { if (event.getDropSuccess()) { repaint(); // 画线 } } public void dragEnter(DragSourceDragEvent event) { DragSourceContext context = event.getDragSourceContext(); context.setCursor(DragSource.DefaultLinkDrop); } public void dragExit(DragSourceEvent event) { } public void dragOver(DragSourceDragEvent event) { } public void dropActionChanged(DragSourceDragEvent event) { } }
class MyDragGestureListener implements DragGestureListener { public void dragGestureRecognized(DragGestureEvent event) { Point pt = event.getDragOrigin(); IMapObject obj = ObjectPanel.this.getMap().getChildAt(pt); if (obj != null && (obj instanceof ILinkable)) { ILinkable src = (ILinkable) obj; if (src.willAccept(null)) { dragSource.startDrag(event, DragSource.DefaultLinkDrop, (Transferable) src, new MyDragSourceListener()); setDragLine(src, new DefaultLinkable(pt)); repaint(); return; } } } }
class MyDropTargetListener implements DropTargetListener { public void dragEnter(DropTargetDragEvent event) { } public void dragExit(DropTargetEvent event) { bDragLine = false; repaint(); } public void dragOver(DropTargetDragEvent event) { if (!event .isDataFlavorSupported(MapperTreeNode.mapperTreeNodeFlavor) && !event .isDataFlavorSupported(DefaultLinkable.linkableFlavor) && !event .isDataFlavorSupported(DefaultLinkable.localLinkableFlavor)) { return; } Point pt = event.getLocation(); Transferable t = event.getTransferable(); // JDK 1.5 ILinkable src = DefaultLinkable.getLinkable(t); ILinkable dst = new DefaultLinkable(pt); IMapObject obj = ObjectPanel.this.getMap().getChildAt(pt); if (obj != null && (obj instanceof ILinkable)) dst = (ILinkable) obj; if (dst.willAccept(src)) { dragLine = new LinkLine(src, dst); bDragLine = true; ObjectPanel.this.repaint(); } } public void drop(DropTargetDropEvent event) { Transferable t = event.getTransferable(); ILinkable src = DefaultLinkable.getLinkable(t); Point pt = event.getLocation(); IMapObject dst = ObjectPanel.this.getMap().getChildAt(pt); if (dst != null && (dst instanceof ILinkable)) if (((ILinkable) dst).willAccept(src)) addLink(src, (ILinkable) dst); map.repaint(); } public void dropActionChanged(DropTargetDragEvent event) { } }
class MyMouseListener implements MouseListener { public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { IMapObject obj = ObjectPanel.this.getMap().getChildAt(e.getPoint()); if (null != obj) objectSelected(obj); else objectSelected(map); } public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) { ptPopup = e.getPoint(); IMapObject obj = ObjectPanel.this.getMap().getChildAt(ptPopup); JPopupMenu popup = new MyPopupMenu(obj); popup.show(e.getComponent(), e.getX(), e.getY()); ObjectPanel.this.repaint(); } } } |
鼠标事件用于设置连线的属性,可在连线上创建函数,用于数据处理。
-
- 连线定义
绘制连线的时候需要进行连线的属性定义。
import java.util.ArrayList; import java.util.List; /** * 连线属性 * @author song0 * */ public class lineMapping {
private String id;//连线ID
private String parentId;//父级连线ID
private List<String> childNodeId;//子连线ID集合
private String sourceNode;//源xpath或jpath 含有@代表属性 没有@代表节点
private String targetNode;//目标xpath或jpath 含有@代表属性 没有@代表节点
private String lineType;//点对点one,循环for
private String sourceType;//xml,json
private String targetType;//xml,json
private List<String> function;//函数列表
public lineMapping() { childNodeId=new ArrayList<String>(); function=new ArrayList<String>(); } public String getSourceNode() { return sourceNode; } public void setSourceNode(String sourceNode) { this.sourceNode = sourceNode; } public String getTargetNode() { return targetNode; } public void setTargetNode(String targetNode) { this.targetNode = targetNode; } public String getLineType() { return lineType; } public void setLineType(String lineType) { this.lineType = lineType; } public String getSourceType() { return sourceType; } public void setSourceType(String sourceType) { this.sourceType = sourceType; } public String getTargetType() { return targetType; } public void setTargetType(String targetType) { this.targetType = targetType; } public List<String> getFunction() { return function; } public void setFunction(List<String> function) { this.function = function; }
public void addFunction(String function) { this.function.add(function); }
public String getFunction(int index) { return this.function.get(index); } public String getId() { return id; |