如何在C#中将JSON转换为XML或将XML转换为JSON?

331

我开始使用Json.NET将JSON格式的字符串转换为对象,或者反过来。我不确定在Json.NET框架中是否可以将JSON格式的字符串转换为XML格式,或者反之亦然?


请注意,正如StaxMan所说,如果元素节点中有空格,则xml将忽略它。例如,“学生ID”:11000由于属性名称中有空格,因此不会出现在xml结果中。xml不接受元素节点内有空格的情况。 - Daniel B
快速回答:如果您查看文档,可以将JSON转换为Xml,网址为https://www.newtonsoft.com/json/help/html/ConvertJsonToXml.htm,将`Xml转换为JSON`,网址为https://www.newtonsoft.com/json/help/html/ConvertXmlToJson.htm。 - Vinod Srivastav
14个回答

483

是的,可以使用JsonConvert类,该类包含用于此特定目的的帮助方法:

// To convert an XML node contained in string xml into a JSON string   
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
string jsonText = JsonConvert.SerializeXmlNode(doc);

// To convert JSON text contained in string json into an XML node
XmlDocument doc = JsonConvert.DeserializeXmlNode(json);

文档在这里:使用Json.NET将JSON和XML相互转换


3
我找不到这个类。我使用的是NewtonSoft Json.net 3.5。 - David.Chu.ca
3
看起来,JSON.NET 3.5 将此功能移至 Newtonsoft.Json.Converters.XmlNodeConverter 类中:http://james.newtonking.com/projects/json/help/html/T_Newtonsoft_Json_Converters_XmlNodeConverter.htm - David Brown
4
提供信息,这里可能存在一个潜在问题。当我将一个XML节点数组转换为JSON时,它会在JSON中生成一个数组。但是,当我遍历一个包含1个节点的XML节点数组时,JSON转换不再格式化为数组。这里会导致只有一个元素的XML数组在转换成JSON后会丢失。 - Levitikon
3
惊喜!这就是XML和JSON之间的阻抗,也是为什么我认为直接在两者之间进行转换不是一个好主意的原因。但是,嘿,有很多开发人员在这里强烈反对我的观点(根据我的回答被踩的情况),他们并不在意这些意外数据转换或潜在数据丢失... - StaxMan
7
@StaxMan:我认为每个人都可以同意,没有一种标准化的方法来将XML文档表示为JSON格式。你的回答可能会被投票否决,因为它实际上并没有回答问题。原帖并不是在问他是否应该进行转换,而是在问他是否可以使用已有工具进行转换。 - David Brown
显示剩余7条评论

60

是的,你可以这样做(我也这么做),但要注意在转换时可能会出现一些悖论,并进行适当处理。您不能自动符合所有接口可能性,并且在控制转换方面具有有限的内置支持-许多JSON结构和值不能自动双向转换。请记住,我正在使用Newtonsoft JSON库和MS XML库的默认设置,因此结果可能会有所不同:

XML -> JSON

  1. 所有数据都变成字符串数据(例如,您始终会得到"false"而不是false"0"而不是0)。显然,在某些情况下,JavaScript会以不同的方式处理它们。
  2. 子元素可以变成嵌套对象{}或嵌套数组[ {} {} ...],具体取决于是否只有一个或多个XML子元素。您将以不同的方式在JavaScript等中使用这两种类型。符合相同模式的不同XML示例可以以这种方式产生实际上不同的JSON结构。您可以为元素添加属性json:Array='true',以在某些情况下解决此问题(但不一定适用于所有情况)。
  3. 您的XML必须是相当规范的。我注意到它不需要完全符合W3C标准,但必须遵循两个强制执行的XML标准:1.您必须有一个根元素;2.您不能以数字开头的元素名称时使用Newtonsoft和MS库时发现的标准之一。
  4. 在旧版本中,空元素无法转换为JSON。它们会被忽略。空元素不会变成"element":null

一项新的更新改变了如何处理null(感谢Jon Story指出):https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_NullValueHandling.htm

JSON -> XML

  1. 您需要一个顶级对象,以便转换为根XML元素,否则解析器将失败。
  2. 您的对象名称不能以数字开头,因为它们无法转换为元素(XML在技术上比这更严格),但我可以“打破”其他一些元素命名规则。

请随时提及您注意到的任何其他问题,我已经开发了自己的自定义程序来准备和清理字符串,以便在JSON和XML之间进行转换。您的情况可能需要预处理/清理,也可能不需要。正如StaxMan所提到的那样,您的情况可能实际上需要在对象之间进行转换......这可能涉及适当的接口以及大量的case语句/等,以处理我上面提到的细节。


2
这篇文章很好地阐述了我的简短回答(在某些时候被严重贬低)的基础——如果你进行盲目的直接转换,那么会有许多许多陷阱。它们可能不会对特定用途造成阻碍,但对其他人来说也可能非常恶劣。 - StaxMan
2
关于XML转JSON的第4点:您可以使用NullValueHandling属性来指定应明确包含空值 - https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_NullValueHandling.htm - Jon Story
这篇评论中问题的描述适用于所有将JSON转换为XML或反之的算法实现。一旦我们接受了不可能同时实现完美的双向保真度,以及输入和输出同时满足"任意一方"或"约束性"(预先指定的模式/格式)的事实,那么这个问题就在一般情况下得到了解决。 - DALDEI

44

你也可以使用 .NET Framework 进行以下转换:

JSON 到 XML:通过使用 System.Runtime.Serialization.Json

var xml = XDocument.Load(JsonReaderWriterFactory.CreateJsonReader(
    Encoding.ASCII.GetBytes(jsonString), new XmlDictionaryReaderQuotas()));

XML转JSON:通过使用System.Web.Script.Serialization

var json = new JavaScriptSerializer().Serialize(GetXmlData(XElement.Parse(xmlString)));

private static Dictionary<string, object> GetXmlData(XElement xml)
{
    var attr = xml.Attributes().ToDictionary(d => d.Name.LocalName, d => (object)d.Value);
    if (xml.HasElements) attr.Add("_value", xml.Elements().Select(e => GetXmlData(e)));
    else if (!xml.IsEmpty) attr.Add("_value", xml.Value);

    return new Dictionary<string, object> { { xml.Name.LocalName, attr } };
}

在 GetXmlData 上出现错误 "The name 'GetXmlData' does not exist in the current context" 是否有我遗漏的 using 指令? - TimSmith-Aardwolf
6
@TimSmith-Aardwolf,这里是你需要的全部代码。如果要使用 System.Web.Script.Serialization,则需要在引用中添加 System.Web.Extensions 程序集。 - Termininja
@Termininja,完美,谢谢。 - cracker
@VinodSrivastav 如果您仔细阅读问题,就会明白作者不确定如何进行转换,只是尝试一些库,其中之一是json.net。我的答案甚至更好,因为我说不需要使用任何额外的库来实现这个目标;) - Termininja
@Termininja 如果你查看历史记录,你会发现这个问题已经被修改过了,最初的问题是“如何使用Json.NET将JSON转换为XML或XML转换为JSON”,所以它被特别标记为json.net,后来他甚至回答了这个问题https://dev59.com/LXRA5IYBdhLWcg3wzhfZ#816095所以假设“不确定...正在尝试一些库”是错误的。尽管如果第一个尝试与`json.net`上下文相关,那么这可以成为另一种答案,就像这里https://dev59.com/LXRA5IYBdhLWcg3wzhfZ#814027。 - Vinod Srivastav
显示剩余2条评论

38

我不确定进行这样的转换是否有意义(是的,许多人这么做,但大多数情况下是为了将方形钉子塞进圆孔)- 存在结构性阻抗失配,并且转换是有损的。因此,我建议不要进行格式到格式的转换。

但是如果您真的需要这么做,请先将 JSON 转换为对象,然后再将对象转换为 XML(反向转换也是如此)。直接进行转换会导致丑陋的输出,信息的丢失或可能两者都有。


1
尽管你的答案被否定了,但我很高兴它在这里。我想进行转换并考虑跳过C#中间对象,但现在不太确定了。否则,我需要基于XSD生成C#对象,因为它纯粹只是为了转换目的而看起来像是浪费层(和精力)。如果你有更多关于它如何丢失的例子或细节,那就太好了。 - CRice
不知道为什么这个被踩了。我目前正在修复我们产品中涉及几个XML <-> JSON转换步骤的一堆错误。大多数是由于从JSON转换为XML时丢失数字类型引起的。 - rikkit
残酷的真相,有用的答案。 - FailedUnitTest
@CRice 太晚了几年,但保留传输对象在某种程度上保留了XML模式。例如,正如Levitikon所提出的那样,如果您尝试转换具有单元素数组的XML文档,则JSON转换器无法知道它是数组,除非它来自具有数组类型的传输对象。 - jpaugh
1
Newtonsoft.JSON的XmlNodeConverter具有一个配置选项,可以避免在从JSON转换为XML再转换回JSON时出现此问题,但它无法捕获原始格式为XML的情况。 - jpaugh
我喜欢你将JSON转换为对象再转换为XML的方法,但是为了反驳你的观点,旧系统使用的供应商提供SOAP XML响应,因此当前系统是根据这个设计的。现在旧供应商被新供应商取代,新供应商以JSON格式提供数据,所以与其编写全新的应用程序,不如将JSON转换为XML并使用相同的旧方法和存储过程。这样做有意义吗? - rahularyansharma

33

感谢David Brown的答案。在我使用JSON.Net 3.5的情况下,转换方法在JsonConvert静态类中:

XmlNode myXmlNode = JsonConvert.DeserializeXmlNode(myJsonString); // is node not note
// or .DeserilizeXmlNode(myJsonString, "root"); // if myJsonString does not have a root
string jsonString = JsonConvert.SerializeXmlNode(myXmlNode);

4
如果你的数据是一个数组,那么你需要像这样做:JsonConvert.DeserializeXmlNode("{"Row":" + json + "}", "root").ToXmlString(),否则你会得到一个“XmlNodeConverter can only convert JSON that begins with an object.”的异常。 - Mitchell Skurnik
是的,而且你不能以数字开头。JsonConvert.DeserializeXmlNode("{"1Row":" + json + "}", "root").ToXmlString() 将会失败。 - DaFi4
以上的回答和 @mitchell 的评论帮助了我。 - Ajay2707

9

我花了很长时间寻找替代代码,以期不使用外部程序集/项目。在查看DynamicJson项目的源代码后,我想到了以下解决方案:

public XmlDocument JsonToXML(string json)
{
    XmlDocument doc = new XmlDocument();

    using (var reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(json), XmlDictionaryReaderQuotas.Max))
    {
        XElement xml = XElement.Load(reader);
        doc.LoadXml(xml.ToString());
    }

    return doc;
}

注意:出于XPath的目的,我希望使用XmlDocument而不是XElement。

此外,这段代码显然只能将JSON转换为XML,有各种方法可以做相反的转换。


2
最近我需要在SQLCLR中完成这个任务,但不能依赖其他程序,所以我硬着头皮写了这个json-to-xml转换例程,它非常简单,只有大约20行代码。 - gordy
如何从 XML 中删除类型? - cracker

6
这里是将XML转换为JSON的完整C#代码:

以下是完整的C#代码,可用于将XML转换为JSON:

public static class JSon
{
public static string XmlToJSON(string xml)
{
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);

    return XmlToJSON(doc);
}
public static string XmlToJSON(XmlDocument xmlDoc)
{
    StringBuilder sbJSON = new StringBuilder();
    sbJSON.Append("{ ");
    XmlToJSONnode(sbJSON, xmlDoc.DocumentElement, true);
    sbJSON.Append("}");
    return sbJSON.ToString();
}

//  XmlToJSONnode:  Output an XmlElement, possibly as part of a higher array
private static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName)
{
    if (showNodeName)
        sbJSON.Append("\"" + SafeJSON(node.Name) + "\": ");
    sbJSON.Append("{");
    // Build a sorted list of key-value pairs
    //  where   key is case-sensitive nodeName
    //          value is an ArrayList of string or XmlElement
    //  so that we know whether the nodeName is an array or not.
    SortedList<string, object> childNodeNames = new SortedList<string, object>();

    //  Add in all node attributes
    if (node.Attributes != null)
        foreach (XmlAttribute attr in node.Attributes)
            StoreChildNode(childNodeNames, attr.Name, attr.InnerText);

    //  Add in all nodes
    foreach (XmlNode cnode in node.ChildNodes)
    {
        if (cnode is XmlText)
            StoreChildNode(childNodeNames, "value", cnode.InnerText);
        else if (cnode is XmlElement)
            StoreChildNode(childNodeNames, cnode.Name, cnode);
    }

    // Now output all stored info
    foreach (string childname in childNodeNames.Keys)
    {
        List<object> alChild = (List<object>)childNodeNames[childname];
        if (alChild.Count == 1)
            OutputNode(childname, alChild[0], sbJSON, true);
        else
        {
            sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ ");
            foreach (object Child in alChild)
                OutputNode(childname, Child, sbJSON, false);
            sbJSON.Remove(sbJSON.Length - 2, 2);
            sbJSON.Append(" ], ");
        }
    }
    sbJSON.Remove(sbJSON.Length - 2, 2);
    sbJSON.Append(" }");
}

//  StoreChildNode: Store data associated with each nodeName
//                  so that we know whether the nodeName is an array or not.
private static void StoreChildNode(SortedList<string, object> childNodeNames, string nodeName, object nodeValue)
{
    // Pre-process contraction of XmlElement-s
    if (nodeValue is XmlElement)
    {
        // Convert  <aa></aa> into "aa":null
        //          <aa>xx</aa> into "aa":"xx"
        XmlNode cnode = (XmlNode)nodeValue;
        if (cnode.Attributes.Count == 0)
        {
            XmlNodeList children = cnode.ChildNodes;
            if (children.Count == 0)
                nodeValue = null;
            else if (children.Count == 1 && (children[0] is XmlText))
                nodeValue = ((XmlText)(children[0])).InnerText;
        }
    }
    // Add nodeValue to ArrayList associated with each nodeName
    // If nodeName doesn't exist then add it
    List<object> ValuesAL;

    if (childNodeNames.ContainsKey(nodeName))
    {
        ValuesAL = (List<object>)childNodeNames[nodeName];
    }
    else
    {
        ValuesAL = new List<object>();
        childNodeNames[nodeName] = ValuesAL;
    }
    ValuesAL.Add(nodeValue);
}

private static void OutputNode(string childname, object alChild, StringBuilder sbJSON, bool showNodeName)
{
    if (alChild == null)
    {
        if (showNodeName)
            sbJSON.Append("\"" + SafeJSON(childname) + "\": ");
        sbJSON.Append("null");
    }
    else if (alChild is string)
    {
        if (showNodeName)
            sbJSON.Append("\"" + SafeJSON(childname) + "\": ");
        string sChild = (string)alChild;
        sChild = sChild.Trim();
        sbJSON.Append("\"" + SafeJSON(sChild) + "\"");
    }
    else
        XmlToJSONnode(sbJSON, (XmlElement)alChild, showNodeName);
    sbJSON.Append(", ");
}

// Make a string safe for JSON
private static string SafeJSON(string sIn)
{
    StringBuilder sbOut = new StringBuilder(sIn.Length);
    foreach (char ch in sIn)
    {
        if (Char.IsControl(ch) || ch == '\'')
        {
            int ich = (int)ch;
            sbOut.Append(@"\u" + ich.ToString("x4"));
            continue;
        }
        else if (ch == '\"' || ch == '\\' || ch == '/')
        {
            sbOut.Append('\\');
        }
        sbOut.Append(ch);
    }
    return sbOut.ToString();
 }
}

要将给定的XML字符串转换为JSON,只需调用以下XmlToJSON()函数。
string xml = "<menu id=\"file\" value=\"File\"> " +
              "<popup>" +
                "<menuitem value=\"New\" onclick=\"CreateNewDoc()\" />" +
                "<menuitem value=\"Open\" onclick=\"OpenDoc()\" />" +
                "<menuitem value=\"Close\" onclick=\"CloseDoc()\" />" +
              "</popup>" +
            "</menu>";

string json = JSON.XmlToJSON(xml);
// json = { "menu": {"id": "file", "popup": { "menuitem": [ {"onclick": "CreateNewDoc()", "value": "New" }, {"onclick": "OpenDoc()", "value": "Open" }, {"onclick": "CloseDoc()", "value": "Close" } ] }, "value": "File" }}

3

试试这个函数。我刚写完它,还没有多少机会测试,但我的初步测试很有前途。

public static XmlDocument JsonToXml(string json)
{
    XmlNode newNode = null;
    XmlNode appendToNode = null;
    XmlDocument returnXmlDoc = new XmlDocument();
    returnXmlDoc.LoadXml("<Document />");
    XmlNode rootNode = returnXmlDoc.SelectSingleNode("Document");
    appendToNode = rootNode;

    string[] arrElementData;
    string[] arrElements = json.Split('\r');
    foreach (string element in arrElements)
    {
        string processElement = element.Replace("\r", "").Replace("\n", "").Replace("\t", "").Trim();
        if ((processElement.IndexOf("}") > -1 || processElement.IndexOf("]") > -1) && appendToNode != rootNode)
        {
            appendToNode = appendToNode.ParentNode;
        }
        else if (processElement.IndexOf("[") > -1)
        {
            processElement = processElement.Replace(":", "").Replace("[", "").Replace("\"", "").Trim();
            newNode = returnXmlDoc.CreateElement(processElement);
            appendToNode.AppendChild(newNode);
            appendToNode = newNode;
        }
        else if (processElement.IndexOf("{") > -1 && processElement.IndexOf(":") > -1)
        {
            processElement = processElement.Replace(":", "").Replace("{", "").Replace("\"", "").Trim();
            newNode = returnXmlDoc.CreateElement(processElement);
            appendToNode.AppendChild(newNode);
            appendToNode = newNode;
        }
        else
        {
            if (processElement.IndexOf(":") > -1)
            {
                arrElementData = processElement.Replace(": \"", ":").Replace("\",", "").Replace("\"", "").Split(':');
                newNode = returnXmlDoc.CreateElement(arrElementData[0]);
                for (int i = 1; i < arrElementData.Length; i++)
                {
                    newNode.InnerText += arrElementData[i];
                }

                appendToNode.AppendChild(newNode);
            }
        }
    }

    return returnXmlDoc;
}

3
JSON 字符串转换为 XML,可以尝试以下方法:
    public string JsonToXML(string json)
    {
        XDocument xmlDoc = new XDocument(new XDeclaration("1.0", "utf-8", ""));
        XElement root = new XElement("Root");
        root.Name = "Result";

        var dataTable = JsonConvert.DeserializeObject<DataTable>(json);
        root.Add(
                 from row in dataTable.AsEnumerable()
                 select new XElement("Record",
                                     from column in dataTable.Columns.Cast<DataColumn>()
                                     select new XElement(column.ColumnName, row[column])
                                    )
               );


        xmlDoc.Add(root);
        return xmlDoc.ToString();
    }

如果需要将 XML 转换为 JSON,可以尝试以下方法:

    public string XmlToJson(string xml)
    {
       XmlDocument doc = new XmlDocument();
       doc.LoadXml(xml);

       string jsonText = JsonConvert.SerializeXmlNode(doc);
       return jsonText;
     }

你能告诉我为什么不能直接使用DeserializeXNode且代码形式为XNode node = JsonConvert.DeserializeXNode(json, "Root"),因为在另一个中你使用了SerializeXmlNode吗? - Vinod Srivastav

3

这里有一个简单的代码片段,可以将XmlNode(递归地)转换成哈希表,并将多个相同子项组合成数组(ArrayList)。 大多数JSON库通常接受哈希表作为JSON的转换格式。

protected object convert(XmlNode root){
    Hashtable obj = new Hashtable();
    for(int i=0,n=root.ChildNodes.Count;i<n;i++){
        object result = null;
        XmlNode current = root.ChildNodes.Item(i);

        if(current.NodeType != XmlNodeType.Text)
            result = convert(current);
        else{
            int resultInt;
            double resultFloat;
            bool resultBoolean;
            if(Int32.TryParse(current.Value, out resultInt)) return resultInt;
            if(Double.TryParse(current.Value, out resultFloat)) return resultFloat;
            if(Boolean.TryParse(current.Value, out resultBoolean)) return resultBoolean;
            return current.Value;
        }

        if(obj[current.Name] == null)
            obj[current.Name] = result;
        else if(obj[current.Name].GetType().Equals(typeof(ArrayList)))
            ((ArrayList)obj[current.Name]).Add(result);
        else{
            ArrayList collision = new ArrayList();
            collision.Add(obj[current.Name]);
            collision.Add(result);
            obj[current.Name] = collision;
        }
    }

    return obj;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接