本篇算法的研究完全没有使用第三方类库来实现,完全使用读取字节控制转换的方法。想研究算法的读者们可以阅读此文章。
XML文件与Json文件在线互转:http://www.bejson.com/xml2json
在研究Json格式数据转换成Xml格式的数据之前,首先,我们需要了解在Json文件中约定的键的格式和XML文件的对应。给下面一个Json文件和一个Xml文件,先观察一下两者的对应关系。
Json文件:
{
"Schools": {
"School": [
{
"-name": "南京工业大学",
"-location": "南京",
"Department": {
"-name": "测绘科学与技术学院",
"Profession": [
"地理信息科学",
{
"Pro": [
"测绘工程",
"土木工程"
],
"Other": "其他专业"
},
"轨道工程"
]
}
},
{
"-name": "复旦大学",
"-location": "上海",
"Department": {
"-name": "计算机科学与技术学院",
"Profession": [
"计算机网络",
{
"-name": "kdkkds",
"#text": "计算机科学与技术"
},
"计算机软件"
]
}
},
{
"-name": "北京大学",
"-location": "北京",
"Department": {
"-name": "北大软微",
"Profession": [
"软件工程",
"微电子",
]
}
}
]
}
}
Xml文件:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Schools>
<School name="南京工业大学" location="南京">
<Department name="测绘科学与技术学院">
<Profession>地理信息科学</Profession>
<Profession>
<Pro>测绘工程</Pro>
<Pro>土木工程</Pro>
<Other>其他专业</Other>
</Profession>
<Profession>轨道工程</Profession>
</Department>
</School>
<School name="复旦大学" location="上海">
<Department name="计算机科学与技术学院">
<Profession>计算机网络</Profession>
<Profession name="kdkkds">计算机科学与技术</Profession>
<Profession>计算机软件</Profession>
</Department>
</School>
<School name="北京大学" location="北京">
<Department name="北大软微">
<Profession>软件工程</Profession>
<Profession>微电子</Profession>
</Department>
</School>
</Schools>
我们从上面的Json文件和Xml文件中可以观察到以下约定关系:
"Profession": [
"软件工程",
"微电子",
]
对应于:
<Profession>软件工程</Profession>
<Profession>微电子</Profession>
了解了以上的对应关系之后,我们可以进入我们的主题了,正式开始研究Json格式的数据转换成Xml的算法。
首先我们通过不断观察对比Json文件和Xml文件,可以先初步得到以下重要信息:
"Profession": [
"计算机网络",
{
"-name": "kdkkds",
"#text": "计算机科学与技术"
},
"计算机软件"
]
对应于:
<Profession>计算机网络</Profession>
<Profession name="kdkkds">计算机科学与技术</Profession>
<Profession>计算机软件</Profession>
正因如此,我们需要在转换的过程中需要保存这个Json数组的名字“Profession”,以供写入Xml使用。
为了方便使用写入过程中的信息,这里面定义两个栈来存储符号和Json数组名信息:
public static Stack<string> signStack = new Stack<string>();//存放符号栈
public static Stack<string> jsonArrStack = new Stack<string>();//存放数组对象的名字的栈
另外需要用到的临时变量:
bool isKey = true;//用来判断是否是key
bool isTextNode = false;//用来判断是否是值节点
bool isAlreadyWriteFirstStart = false; //用来判断是否是值节点数组
string tempAttr = "";//存放临时属性
string tempElement = "";//存放临时元素名
bool isTempElementBefore = false;//用来判断前一个引号中的数据是否是Xml文件中的元素名
bool isCommaBefore = false;//用来判断某项操作前的一个符号是否是一个逗号
List<byte> quoteContentBytes = new List<byte>();//存放引号中的数据
使用FileStream逐步读取Json文件中的字节,使用XmlWriter将读到的信息写入Xml文件:
using (FileStream fsRead = new FileStream(path, FileMode.Open, FileAccess.Read))
{
//为XmlReader对象设置settings
XmlReaderSettings settingsReader = new XmlReaderSettings();
settingsReader.IgnoreComments = true;
settingsReader.IgnoreWhitespace = true;
//为XmlWriter对象设置settings
XmlWriterSettings settingsWriter = new XmlWriterSettings();
settingsWriter.Indent = true;//要求缩进
settingsWriter.Encoding = new UTF8Encoding(true);
settingsWriter.NewLineChars = Environment.NewLine; //设置换行符
string filePath = path.Substring(0, path.LastIndexOf("."));
string writerPath = filePath + ".xml";
using (XmlWriter xmlWriter = XmlWriter.Create(writerPath, settingsWriter))
{
xmlWriter.WriteStartDocument(false);
int bt = fsRead.ReadByte();
while (bt != -1)
{
........//主要算法内容
bt = fsRead.ReadByte();
}
}
}
算法核心逻辑如下:
代码如下:
//为冒号“:”
if (bt == 58)
isKey = false;
if (bt == 34)//为引号
{
int b = fsRead.ReadByte();
//读取引号中的内容
while (b != 34)
{
quoteContentBytes.Add(Convert.ToByte(b));
b = fsRead.ReadByte();
}
string result = System.Text.Encoding.UTF8.GetString(quoteContentBytes.ToArray());
if (signStack.Peek() == "[")
{
if (!isAlreadyWriteFirstStart)
xmlWriter.WriteStartElement(jsonArrStack.Peek());
isAlreadyWriteFirstStart = false;
xmlWriter.WriteString(result);
xmlWriter.WriteEndElement();
}
else
{
if (isKey)//为key
{
//是属性Attribute
if (result[0].ToString() == "-")
{
string attr = Regex.Replace(result, "-", "");
tempAttr = attr;
isTempElementBefore = false;
}
else if (result != "#text")//是子节点
{
tempElement = result;
xmlWriter.WriteStartElement(tempElement);
isTempElementBefore = true;
}
else//是包含Value的节点
isTextNode = true;
}
else//为值
{
if (isTextNode || isTempElementBefore)
{
xmlWriter.WriteString(result);
isTextNode = false;
if (isTempElementBefore)
xmlWriter.WriteEndElement();
}
else
xmlWriter.WriteAttributeString(tempAttr, result);
}
}
quoteContentBytes.Clear();//清空
isCommaBefore = false;
}
else if (bt == 123)//为“{”
{
//若是逗号,则表示是一个[]对象中的除了第一个的其他对象。
if (isCommaBefore)
xmlWriter.WriteStartElement(jsonArrStack.Peek());
signStack.Push("{");//符号进栈
isKey = true;
isAlreadyWriteFirstStart = false;
isTempElementBefore = false;
}
else if (bt == 91)
{
signStack.Push("[");//符号进栈
jsonArrStack.Push(tempElement);//此处tempElement存放便是“[”之前的一个引号中的数据
isAlreadyWriteFirstStart = true;
isTempElementBefore = false;
}
else if (bt == 125)//为“}”时,jsonArrStack退栈,并写入WriteEndElement
{
string t = signStack.Pop();//符号出栈
//自检,用于检测是否有括号不匹配现象
if ((bt == 125 && t != "{") || (bt == 93 && t != "["))
Console.WriteLine(tempElement);
//如果是最后一个“{”,对应于第一个进栈的,也不写关闭元素;
if (signStack.Count > 1)
{
xmlWriter.WriteEndElement();
isAlreadyWriteFirstStart = signStack.Peek() != "[";
}
}
else if (bt == 93) //为“]”时,jsonArrStack退栈
{
string t = signStack.Pop();//符号出栈
//自检,用于检测是否有括号不匹配现象
if ((bt == 125 && t != "{") || (bt == 93 && t != "["))
Console.WriteLine(tempElement);
//若为“]”,则arrStack出栈
jsonArrStack.Pop();
isAlreadyWriteFirstStart = signStack.Peek() != "[";
}
//逗号
if (bt == 44)
{
isKey = true;
isCommaBefore = true;//记录下一次读取数据之前的符号为逗号
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
namespace JSONDataToXML
{
class Program
{
public static Stack<string> signStack = new Stack<string>();//存放符号栈
public static Stack<string> jsonArrStack = new Stack<string>();//存放数组对象的名字的栈
public static List<byte> quoteContentBytes = new List<byte>();//存放引号中的数据
bool isKey = true;//用来判断是否是key
bool isTextNode = false;//用来判断是否是值节点
bool isAlreadyWriteFirstStart = false; //用来判断是否是值节点数组
int valueArrDeepth = 0;
string tempAttr = "";//存放临时属性
string tempElement = "";//存放临时元素名
bool isTempElementBefore = false;
bool isCommaBefore = false;//用来判断某项操作前的一个符号是否是一个逗号
static void Main(string[] args)
{
Console.WriteLine("请输入Json文件路径:");
string path = Console.ReadLine();
Stopwatch sw = new Stopwatch();
sw.Start();
//解析Json
JsonDataToXml(path);
sw.Stop();
Console.WriteLine("用时:" + sw.Elapsed.ToString());
Console.WriteLine("解析成功!");
Console.ReadKey();
}
public static void JsonDataToXml(string path)
{
using (XmlWriter xmlWriter = XmlWriter.Create(writerPath, settingsWriter))
{
xmlWriter.WriteStartDocument(false);
int bt = fsRead.ReadByte();
bool isKey = true;//用来判断是否是key
bool isTextNode = false;//用来判断是否是值节点
bool isAlreadyWriteFirstStart = false; //用来判断是否是值节点数组
string tempAttr = "";//存放临时属性
string tempElement = "";//存放临时元素名
bool isTempElementBefore = false;
bool isCommaBefore = false;//用来判断某项操作前的一个符号是否是一个逗号
while (bt != -1)
{
//为冒号“:”
if (bt == 58)
isKey = false;
if (bt == 34)//为引号
{
int b = fsRead.ReadByte();
//读取引号中的内容
while (b != 34)
{
quoteContentBytes.Add(Convert.ToByte(b));
b = fsRead.ReadByte();
}
string result = System.Text.Encoding.UTF8.GetString(quoteContentBytes.ToArray());
if (signStack.Peek() == "[")
{
if (!isAlreadyWriteFirstStart)
xmlWriter.WriteStartElement(jsonArrStack.Peek());
isAlreadyWriteFirstStart = false;
xmlWriter.WriteString(result);
xmlWriter.WriteEndElement();
}
else
{
if (isKey)//为key
{
//是属性Attribute
if (result[0].ToString() == "-")
{
string attr = Regex.Replace(result, "-", "");
tempAttr = attr;
isTempElementBefore = false;
}
else if (result != "#text")//是子节点
{
tempElement = result;
xmlWriter.WriteStartElement(tempElement);
isTempElementBefore = true;
}
else//是包含Value的节点
isTextNode = true;
}
else//为值
{
if (isTextNode || isTempElementBefore)
{
xmlWriter.WriteString(result);
isTextNode = false;
if (isTempElementBefore)
xmlWriter.WriteEndElement();
}
else
xmlWriter.WriteAttributeString(tempAttr, result);
}
}
quoteContentBytes.Clear();//清空
isCommaBefore = false;
}
else if (bt == 123)//为“{”
{
//若是逗号,则表示是一个[]对象中的除了第一个的其他对象。
if (isCommaBefore)
xmlWriter.WriteStartElement(jsonArrStack.Peek());
signStack.Push("{");//符号进栈
isKey = true;
isAlreadyWriteFirstStart = false;
isTempElementBefore = false;
}
else if (bt == 91)
{
signStack.Push("[");//符号进栈
jsonArrStack.Push(tempElement);//此处tempElement存放便是“[”之前的一个引号中的数据
isAlreadyWriteFirstStart = true;
isTempElementBefore = false;
}
else if (bt == 125)//为“}”时,jsonArrStack退栈,并写入WriteEndElement
{
string t = signStack.Pop();//符号出栈
//自检,用于检测是否有括号不匹配现象
if ((bt == 125 && t != "{") || (bt == 93 && t != "["))
Console.WriteLine(tempElement);
//如果是最后一个“{”,对应于第一个进栈的,也不写关闭元素;
if (signStack.Count > 1)
{
xmlWriter.WriteEndElement();
isAlreadyWriteFirstStart = signStack.Peek() != "[";
}
}
else if (bt == 93) //为“]”时,jsonArrStack退栈
{
string t = signStack.Pop();//符号出栈
//自检,用于检测是否有括号不匹配现象
if ((bt == 125 && t != "{") || (bt == 93 && t != "["))
Console.WriteLine(tempElement);
//若为“]”,则arrStack出栈
jsonArrStack.Pop();
isAlreadyWriteFirstStart = signStack.Peek() != "[";
}
//逗号
if (bt == 44)
{
isKey = true;
isCommaBefore = true;//记录下一次读取数据之前的符号为逗号
}
bt = fsRead.ReadByte();
}
}
}
}
}
}
结语:本人在研究Json格式转换成Xml格式的时候仅使用自己能想到的Json格式,在此基础上去设计算法,可能会有一些我没有考虑到的情况,若读者发现有代码或逻辑控制有缺陷,可反馈在评论,谢谢。
相应github地址:https://github.com/LuQiJun/Json2Xml