此工程用途:将xml同级属性/子节点按字母序排列重新输出.

源码下载: https://files.cnblogs.com/files/heyang78/XmlAnalyzer-20200526-1.zip

核心类:

Token,此类用于将XML文件中的文本分类:

package com.heyang;

public class Token {
public final static int TYPE_OPEN_ANGLEBRACKET=0; // <
public final static int TYPE_CLOSE_ANGLEBRACKET=1;// >
public final static int TYPE_slant =2; // /
public final static int TYPE_TEXT=3; // text
public final static int TYPE_EQUAL =4; // =
public final static int TYPE_EMPTY_CLOSE =5; // />
public final static int TYPE_END_OPEN =6; // </ private int type;
private String text;
private int index;// Used to remember location public Token(char c,int type) {
this.text=String.valueOf(c);
this.type=type;
} public Token(String word,int type) {
this.text=word;
this.type=type;
} public String toString() {
return String.format("token(text=%s,type=%d,index=%d)", text,type,index);
} public int getType() {
return type;
} public void setType(int type) {
this.type = type;
} public String getText() {
return text;
} public void setText(String text) {
this.text = text;
} public int getIndex() {
return index;
} public void setIndex(int index) {
this.index = index;
}
}

Lexer,此类用于分词:

package com.heyang;

import java.util.ArrayList;
import java.util.List; import org.apache.commons.lang.StringUtils; public class Lexer {
private List<Token> tokens; public Lexer(String inputTxt) {
tokens = new ArrayList<Token>(); String bundle = "";
for (int i = 0; i < inputTxt.length(); i++) {
char c = inputTxt.charAt(i); if (Character.isWhitespace(c)) {
if (StringUtils.isNotEmpty(bundle.trim())) {
addText2Tokens(bundle);
bundle = "";
} continue;
} else if (c == '<') {
int next=i+1;
if(next<inputTxt.length() && inputTxt.charAt(next)=='/') {
addText2Tokens(bundle);
bundle="";
tokens.add(new Token("</",Token.TYPE_END_OPEN));
i++;
}else {
tokens.add(new Token(c, Token.TYPE_OPEN_ANGLEBRACKET));
} } else if (c == '>') {
if (StringUtils.isNotEmpty(bundle)) {
addText2Tokens(bundle);
bundle = "";
} tokens.add(new Token(c, Token.TYPE_CLOSE_ANGLEBRACKET));
}else if (c == '=') {
if (StringUtils.isNotEmpty(bundle)) {
addText2Tokens(bundle);
bundle = "";
} tokens.add(new Token(c, Token.TYPE_EQUAL));
} else if (c == '/') {
int next=i+1;
if(next<inputTxt.length() && inputTxt.charAt(next)=='>') {
addText2Tokens(bundle);
bundle="";
tokens.add(new Token("/>",Token.TYPE_EMPTY_CLOSE));
i++;
}else {
tokens.add(new Token(c, Token.TYPE_slant));
} } else if(c == '\"') {
int idx=i+1; while(idx<inputTxt.length()) {
char cEnd = inputTxt.charAt(idx); if (cEnd == '\"') {
break;
} idx++;
} String sub=inputTxt.substring(i, idx+1);
tokens.add(new Token(sub, Token.TYPE_TEXT));
i=idx;
} else {
bundle += c;
}
} setTokenIndexes();
} private boolean addText2Tokens(String text) {
if(StringUtils.isNotEmpty(text)) {
tokens.add(new Token(text, Token.TYPE_TEXT));
return true;
}else {
return false;
}
} public void setTokenIndexes() {
int idx = 0;
for (Token t : tokens) {
idx++;
t.setIndex(idx);
}
} public void printTokens() {
int idx = 0;
for (Token t : tokens) {
idx++;
t.setIndex(idx);
System.out.println("#" + idx + " " + t.getText());
}
} public String getCompactJsonTxt() {
StringBuilder sb=new StringBuilder(); for (Token t : tokens) {
sb.append(t.getText()+" ");
} return sb.toString();
} public List<Token> getTokens() {
return tokens;
}
}

Node,此类代表一个xml节点:

package com.heyang;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List; public class Node implements Comparable<Node>{
private String text;
private String name;
private List<Node> children;
private List<Property> proterties;
private int depth=0; public int compareTo(Node another) {
return this.name.compareTo(another.name);
} public void addChild(Node n) {
if(children==null) {
children=new ArrayList<Node>();
} children.add(n);
adjustDepth();
} private void adjustDepth() {
if(children==null) {
return;
}
for(Node json:children) {
json.depth=this.depth+1;
json.adjustDepth();
}
} public void addProperty(Property p) {
if(proterties==null) {
proterties=new ArrayList<Property>();
} proterties.add(p);
} public String toString() {
String tabs=getIndentSpace(); StringBuilder sb=new StringBuilder();
sb.append(tabs); sb.append("<"+name);
if(proterties!=null) {
Collections.sort(proterties); for(Property p:proterties) {
sb.append(" "+p.getName()+"="+p.getValue());
}
} if(text==null && children==null) {
sb.append("/>");
return sb.toString();
}else {
sb.append(">");
} if(text!=null) {
sb.append(text);
} if(children!=null) { Collections.sort(children);
for(Node child:children) {
sb.append("\n");
sb.append(child);
}
} if(children!=null) {
sb.append("\n"+tabs+"</"+name+">");
}else {
sb.append("</"+name+">");
} return sb.toString();
} private String getIndentSpace() {
return String.join("", Collections.nCopies(this.depth, " "));
} public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Node> getChildren() {
return children;
}
public void setChildren(List<Node> children) {
this.children = children;
}
public List<Property> getProterties() {
return proterties;
}
public void setProterties(List<Property> proterties) {
this.proterties = proterties;
}
}

property,此类代表xml的属性:

package com.heyang;

public class Property implements Comparable<Property>{
private String name;
private String value; public int compareTo(Property another) {
return this.name.compareTo(another.name);
} public Property(String name,String value) {
this.name=name;
this.value=value;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

TreeBuilder,此类用于自顶向下构建一棵树:

package com.heyang;

import java.util.List;

public class TreeBuilder {
private Node root;
private List<Token> tokens;
private int tokenIdx; public TreeBuilder(List<Token> tokens) throws Exception{
this.tokens=tokens;
this.tokenIdx=0; root=new Node();
parseNode(root);
} private void parseNode(Node parent) throws Exception{
Token token; token=fetchToken();
if(token.getType()!=Token.TYPE_OPEN_ANGLEBRACKET) {
throw new Exception("Expected:'<' actual:"+token.getText()+" "+token);
} token=fetchToken();
if(token.getType()!=Token.TYPE_TEXT) {
throw new Exception("Expected:text actual:"+token.getText()+" "+token);
} // get node name
parent.setName(token.getText()); // get properties
for(;;) {
token=fetchToken();
if(token.getType()!=Token.TYPE_TEXT) {
// 不满足属性条件,退回并退出
returnToken();
break;
} String name=token.getText(); // =
token=fetchToken();
if(token.getType()!=Token.TYPE_EQUAL) {
throw new Exception("Expected:= actual:"+token.getText()+" "+token);
} token=fetchToken();
if(token.getType()!=Token.TYPE_TEXT) {
throw new Exception("Expected:= actual:"+token.getText()+" "+token);
} String value=token.getText(); parent.addProperty(new Property(name,value));
} token=fetchToken();
if(token.getType()==Token.TYPE_EMPTY_CLOSE) {
// 节点结束,无子节点,无文本
return;
}else if(token.getType()==Token.TYPE_CLOSE_ANGLEBRACKET) {
// 存在子节点或文本,继续向下
}else {
// 未正常结束,抛出异常
throw new Exception("Expected:'>' actual:"+token.getText()+" "+token);
} // 取文本或子节点
for(;;) {
token=fetchToken(); if(token.getType()==Token.TYPE_TEXT) {
// 取得文本
parent.setText(token.getText());
}else if(token.getType()==Token.TYPE_OPEN_ANGLEBRACKET) {
// TODO:取子节点,加子节点,递归向下
Node child=new Node();
parent.addChild(child); returnToken();
parseNode(child);
}else {
// 不满足属性条件,退回并退出
returnToken();
break;
}
} token=fetchToken();
if(token.getType()!=Token.TYPE_END_OPEN) {
throw new Exception("Expected:'</' actual:"+token.getText()+" "+token);
} token=fetchToken();
if(token.getType()!=Token.TYPE_TEXT) {
throw new Exception("Expected:text actual:"+token.getText()+" "+token);
} String name=token.getText();
if(!name.equals(parent.getName())) {
throw new Exception("Expected node name:"+parent.getName()+" actual:"+name+" "+token);
} token=fetchToken();
if(token.getType()!=Token.TYPE_CLOSE_ANGLEBRACKET) {
throw new Exception("Expected:'>' actual:"+token.getText()+" "+token);
}
} private Token fetchToken() {
if(tokenIdx>=tokens.size()) {
return null;
}else {
Token t=tokens.get(tokenIdx);
tokenIdx++;
return t;
}
} private void returnToken() {
if(tokenIdx>0) {
tokenIdx--;
}
} public Node getRoot() {
return root;
}
}

最后整合调用:

package com.heyang;

import com.heyang.util.BracketChecker;
import com.heyang.util.CommonUtil;
import com.heyang.util.Renderer; public class EntryPoint {
public static void main(String[] args) {
try {
// Read context from file
String jsonTxt=CommonUtil.readTextFromFile("C:\\hy\\files\\xml\\01.xml");
System.out.println("原文="+jsonTxt); // Is brackets balanced
BracketChecker checker=new BracketChecker();
boolean isBalanced=checker.isBalanced(jsonTxt);
if(isBalanced==false) {
System.out.println(Renderer.paintBrown(checker.getErrMsg()));
return;
} // Parse json to tokens
Lexer lex=new Lexer(jsonTxt);
//System.out.println("紧缩文本="+lex.getCompactJsonTxt());
//lex.printTokens(); // Build tree
TreeBuilder builder=new TreeBuilder(lex.getTokens());
Node root=builder.getRoot();
System.out.println("整形后文本:\n"+root);
}catch(Exception ex) {
System.out.println(Renderer.paintBrown(ex.getMessage()));
ex.printStackTrace();
}
}
}

整形效果:

原文=<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>com.heyang</groupId>  <artifactId>XmlAnalyzer</artifactId>  <version>1.00</version>    <dependencies>        <dependency>            <groupId>ch.qos.logback</groupId>            <artifactId>logback-classic</artifactId>            <version>1.1.11</version>        </dependency>        <dependency>            <groupId>ch.qos.logback</groupId>            <artifactId>logback-core</artifactId>            <version>1.1.11</version>        </dependency>                <dependency>            <groupId>commons-lang</groupId>            <artifactId>commons-lang</artifactId>            <version>2.6</version>        </dependency>    </dependencies></project>
整形后文本:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>XmlAnalyzer</artifactId>
<dependencies>
<dependency>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
<version>1.1.11</version>
</dependency>
<dependency>
<artifactId>logback-core</artifactId>
<groupId>ch.qos.logback</groupId>
<version>1.1.11</version>
</dependency>
<dependency>
<artifactId>commons-lang</artifactId>
<groupId>commons-lang</groupId>
<version>2.6</version>
</dependency>
</dependencies>
<groupId>com.heyang</groupId>
<modelVersion>4.0.0</modelVersion>
<version>1.00</version>
</project>

2020-5-22 解析算术表达式

2020-5-25 解析Json

2002-5-26 解析XML

感觉编译器/解释器的路越走越宽了.

--2020年5月26日--

XmlAnalyzer1.00 源码的更多相关文章

  1. SqlAnalyzer1.00源码

    SQL解析的夙愿今天终于完成,但限于SQL远超算术表达式,Json,XML等的复杂度,只是解析了一部分.形成普适性的SQL解析,仍需倾注精力. 代码下载:https://files.cnblogs.c ...

  2. 【iScroll源码学习00】模拟iScroll

    前言 相信对移动端有了解的朋友对iScroll这个库非常熟悉吧,今天我们就来说下我们移动页面的iScroll化 iScroll是我们必学框架之一,我们这次先根据iScroll功能自己实现其功能,然后再 ...

  3. C# Excel导入、导出【源码下载】

    本篇主要介绍C#的Excel导入.导出. 目录 1. 介绍:描述第三方类库NPOI以及Excel结构 2. Excel导入:介绍C#如何调用NPOI进行Excel导入,包含:流程图.NOPI以及C#代 ...

  4. 一篇文章看懂TPCx-BB(大数据基准测试工具)源码

    TPCx-BB是大数据基准测试工具,它通过模拟零售商的30个应用场景,执行30个查询来衡量基于Hadoop的大数据系统的包括硬件和软件的性能.其中一些场景还用到了机器学习算法(聚类.线性回归等).为了 ...

  5. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

  6. Web应用之LAMP源码环境部署

    一.LAMP环境的介绍 1.LAMP环境的重要性 思索许久,最终还是决定写一篇详细的LAMP的源码编译安装的实验文档,一来是为了给自己一个交代,把技术进行系统的归纳,将技术以极致的形式呈现出来,做为一 ...

  7. 【原】SDWebImage源码阅读(五)

    [原]SDWebImage源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 前面的代码并没有特意去讲SDWebImage的缓存机制,主要是想单独开一章节专门讲 ...

  8. AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization

    这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...

  9. AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization

    本篇是AFNetworking 3.0 源码解读的第四篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...

随机推荐

  1. javascript 字符串对象

    数组转换字符串     tostring() 将数组转换成字符串     join(分隔符)将数组转换成字符串     基本包装类型     基本包装类型 就是把简单数据类型 包装成繁杂数据类型    ...

  2. 创建shell脚本文件

    简单来说脚本就是将需要执行的命令保存到文本中,按照顺序(由上往下执行),它是解释型的,不需要 编译 脚本格式 #!/bin/bash或者#!/bin/env bash开头 第一个shell脚本:hel ...

  3. java 序列化流与反序列化流

    一 对象序列化流ObjectOutputStream ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream.可以使用 ObjectInputStr ...

  4. Linux下gcc/g++中-I(i的大写)、-L和-l

    -I(i的大写)include头文件非标准库中存在的也不是在当前文件夹下的,需要将地址用-i(大写)包含例:-I /home/src/-L用到的函数或操作非标准库下的,需要将库存在的地址用-L包含,库 ...

  5. golang中type关键字使用

    type关键字使用 type是go语法里的重要而且常用的关键字,type绝不只是对应于C/C++中的typedef.搞清楚type的使用,就容易理解go语言中的核心概念struct.interface ...

  6. 精讲RestTemplate第6篇-文件上传下载与大文件流式下载

    本文是精讲RestTemplate第6篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层H ...

  7. Window C盘 占满原因之一

    最近一段时间,突然C盘 莫名奇妙的满了 ,也没有中毒. 最后查找是因为安装了SQL Reporting  的原因 C:\Program Files\Microsoft SQL Server Repor ...

  8. 当Notification和Websocket遇到https、http

    @ 目录 一.http转为https请求 (1)生成证书1(crt证书转tomcat使用的jks) (2)配置证书1 (3)生成证书2 (4)配置证书2 二.Websocket改为https连接 后言 ...

  9. 利用C#实现OPC-UA服务端

    前言 最近接手了一个项目,做一个 OPC-UA 服务端?刚听到这个消息我是一脸懵,发自灵魂的三问“OPC-UA是什么?”.“要怎么做?”.“有什么用?”.我之前都是做互联网相关的东西,这种物联网的还真 ...

  10. extJS--尚

    ExtJS依赖JavaScript,JavaScript推荐两本书:<JavaScript高级程序设计>初阶阶段, <JavaScript设计模式>中级阶段 fun1();// ...