Как конвертировать JSON в XML или XML в JSON?

246

Я начал использовать Json.NET для преобразования строки в формате JSON в объект или наоборот. Я не уверен в структуре Json.NET, возможно ли преобразовать строку в формате JSON в XML и наоборот?

  • 0
    Обратите внимание, как сказал StaxMan, если есть бывший. пространство в узле элемента, оно будет игнорироваться xml. Например "Student Id": 11000 не будет в xml результате bcuz пробела в имени свойства. XML не допускает наличие пробела в элементном узле.
Теги:
json.net

12 ответов

362
Лучший ответ

Да. Использование класса 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 и XML с помощью Json.NET

  • 3
    Я не мог найти этот класс. Я использую NewtonSoft Json.net 3.5.
  • 3
    Похоже, что эта функциональность была перемещена в класс Newtonsoft.Json.Converters.XmlNodeConverter в JSON.NET 3.5: james.newtonking.com/projects/json/help/html/…
Показать ещё 8 комментариев
39

Да, вы можете сделать это (я делаю), но при конвертации обращайте внимание на некоторые парадоксы и действуйте соответствующим образом. Вы не можете автоматически соответствовать всем возможностям интерфейса, и имеется ограниченная встроенная поддержка в управлении conversion- многими структурами JSON, и значения не могут автоматически конвертироваться в обоих направлениях. Помните, что я использую настройки по умолчанию с библиотекой Newtonsoft JSON и библиотекой MS XML, поэтому ваш пробег может варьироваться:

XML → JSON

  1. Все данные становятся строковыми данными (например, вы всегда получите "ложь", а не ложь или "0", а не 0). Очевидно, что в некоторых случаях JavaScript обрабатывает их по-разному.
  2. Дочерние элементы могут стать nested-object {} ИЛИ nested-array [ {} {}...] зависимости от того, существует ли только один или несколько дочерних XML-элементов. Вы бы по-разному использовали эти два в JavaScript и т.д. Различные примеры XML, соответствующего одной и той же схеме, могут таким образом создавать действительно разные структуры JSON. Вы можете добавить атрибут json: Array = 'true' к своему элементу, чтобы обойти это в некоторых (но не обязательно во всех) случаях.
  3. Ваш XML должен быть достаточно правильно сформирован, я заметил, что он не обязательно должен полностью соответствовать стандарту W3C, но 1. у вас должен быть корневой элемент и 2. вы не можете начинать имена элементов с чисел - это два из обязательных стандартов XML Я нашел при использовании библиотек Newtonsoft и MS.
  4. В старых версиях пустые элементы не конвертируются в JSON. Они игнорируются. Пустой элемент не становится "элементом": null

Новое обновление изменяет это (спасибо Джону Стори за указание на это): https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_NullValueHandling.htm

JSON → XML

  1. Вам нужен объект верхнего уровня, который преобразуется в корневой элемент XML, иначе парсер не удастся.
  2. Имена ваших объектов не могут начинаться с цифры, поскольку они не могут быть преобразованы в элементы (XML технически даже более строг, чем этот), но я могу "сойти" с нарушением некоторых других правил именования элементов.

Пожалуйста, не стесняйтесь упоминать о любых других проблемах, которые вы заметили, я разработал свои собственные пользовательские процедуры для подготовки и очистки строк, когда я конвертирую туда-сюда. Ваша ситуация может требовать или не требовать подготовки/очистки. Как упоминает StaxMan, ваша ситуация может фактически потребовать, чтобы вы конвертировали между объектами... это может потребовать соответствующих интерфейсов и набора операторов case/etc для обработки предостережений, которые я упомянул выше.

  • 0
    Это! Хорошая проработка того, на чем был основан мой короткий (и в какой-то момент сильно опущенный) ответ - есть много-много подводных камней, если вы делаете слепое прямое обращение. Они могут не блокировать проблемы для конкретного использования, но также могут быть очень неприятными для других.
  • 1
    Что касается # 4 в XML -> JSON: вы можете использовать свойство NullValueHandling, чтобы указать, что нулевые значения должны быть включены явно - newtonsoft.com/json/help/html/…
30

Вы можете сделать эти преобразования также с .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 } };
}
  • 0
    Я получаю сообщение об ошибке GetXmlData "Имя GetXmlData не существует в текущем контексте". Есть ли какая-либо директива using, которую я пропускаю?
  • 4
    @ TimSmith-Aardwolf, вот весь код, который тебе нужен. Для использования System.Web.Script.Serialization необходимо добавить сборку System.Web.Extensions в списке литературы.
Показать ещё 3 комментария
25

Спасибо за ответ Дэвида Брауна. В моем случае 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 можно конвертировать только JSON, который начинается с объекта. " исключение.
  • 0
    Да, и вы не можете начать с числа. JsonConvert.DeserializeXmlNode ("{\" 1Row \ ":" + json + "}", "root"). Ошибка ToXmlString ()
Показать ещё 1 комментарий
25

Я не уверен, что есть смысл в таком преобразовании (да, многие это делают, но в основном для принудительной квадратной привязки через круглое отверстие) - существует несоответствие структурного импеданса, а преобразование - с потерями. Поэтому я бы рекомендовал против таких преобразований формата в формат.

Но если вы это сделаете, сначала конвертируйте из json в объект, затем из объекта в xml (и наоборот для обратного направления). Выполнение прямого преобразования приводит к уродливому выходу, потере информации или, возможно, обоим.

  • 1
    Даже если ваш ответ был разбит, я рад, что он здесь. Я хочу сделать преобразование и собирался пропустить средние объекты c #, но теперь не уверен. В противном случае мне нужно было бы генерировать объекты c # на основе XSD, и, поскольку это было бы чисто только для целей конверсии, это выглядело как потраченный впустую слой (и усилия). Если у вас есть примеры или более подробно о том, как это с потерями, было бы здорово увидеть.
  • 0
    Не знаю, почему за это проголосовали. В настоящее время я исправляю кучу ошибок, связанных с несколькими шагами преобразования XML <-> JSON в имеющемся у нас продукте. Большинство из них сводятся к потере числовых типов при преобразовании из JSON в XML.
Показать ещё 1 комментарий
8

Я искал долгое время, чтобы найти альтернативный код принятому решению в надежде не использовать внешнюю сборку/проект. Я придумал следующее благодаря исходному коду проекта 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;
}

Примечание. Мне нужен XmlDocument, а не XElement для целей xPath. Кроме того, этот код, очевидно, идет только от JSON к XML, есть разные способы сделать обратное.

  • 0
    Мне нужно было сделать это недавно в SQLCLR и я не мог получить зависимость, поэтому я просто укусил пулю и написал эту процедуру преобразования json в xml , она была на удивление простой и всего около 20 строк кода.
  • 0
    как удалить typr из xml?
5

Вот полный код С# для преобразования 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" }}
4

Попробуйте эту функцию. Я просто написал это и не имел большой возможности проверить его, но мои предварительные тесты являются многообещающими.

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;
}
2

Вот простой фрагмент, который преобразует XmlNode (рекурсивно) в хэш-таблицу и группирует несколько экземпляров одного и того же дочернего элемента в массив (как ArrayList). Hashtable обычно принимается для преобразования в 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;
}
0

Я использовал следующие методы для преобразования JSON в XML

        List<Item> items;
        public void LoadJsonAndReadToXML()
        {
            using (StreamReader r = new StreamReader(@"E:\Json\overiddenhotelranks.json"))
            {
                string json = r.ReadToEnd();
                items = JsonConvert.DeserializeObject<List<Item>>(json);
                ReadToXML();
            }
        }

а также

        public void ReadToXML()
        {    
            try
            {
                var xEle = new XElement("Items",
                            from item in items
                            select new XElement("Item",
                                           new XElement("mhid", item.mhid),
                                           new XElement("hotelName", item.hotelName),
                                           new XElement("destination", item.destination),
                                           new XElement("destinationID", item.destinationID),
                                           new XElement("rank", item.rank),
                                           new XElement("toDisplayOnFod", item.toDisplayOnFod),
                                           new XElement("comment", item.comment),
                                           new XElement("Destinationcode", item.Destinationcode),
                                           new XElement("LoadDate", item.LoadDate)
                                       ));

                xEle.Save("E:\\employees.xml");
                Console.WriteLine("Converted to XML");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadLine();
        }

Я использовал класс с именем Item для представления элементов

    public class Item
    {
        public int mhid { get; set; }
        public string hotelName { get; set; }
        public string destination { get; set; }
        public int destinationID { get; set; }
        public int rank { get; set; }
        public int toDisplayOnFod { get; set; }
        public string comment { get; set; }
        public string Destinationcode { get; set; }
        public string LoadDate { get; set; }

    }

Оно работает....

0

Cinchoo ETL - библиотека с открытым исходным кодом, доступная для простого преобразования Xml в JSON с несколькими строками кода

Xml → JSON:

using (var p = new ChoXmlReader("sample.xml"))
{
    using (var w = new ChoJSONWriter("sample.json"))
    {
        w.Write(p);
    }
}

JSON → Xml:

using (var p = new ChoJsonReader("sample.json"))
{
    using (var w = new ChoXmlWriter("sample.xml"))
    {
        w.Write(p);
    }
}

Оформить статью CodeProject за дополнительной помощью.

Отказ от ответственности: я являюсь автором этой библиотеки.

0

Мне понравился Дэвид Браун, но я получил следующее исключение.

$exception {"There are multiple root elements. Line , position ."} System.Xml.XmlException

Одним из решений было бы модифицировать XML файл с помощью корневого элемента, но это не всегда необходимо, и для потока XML это может оказаться невозможным. Мое решение ниже:

var path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\App_Data"));
var directoryInfo = new DirectoryInfo(path);
var fileInfos = directoryInfo.GetFiles("*.xml");

foreach (var fileInfo in fileInfos)
{
    XmlDocument doc = new XmlDocument();
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ConformanceLevel = ConformanceLevel.Fragment;

    using (XmlReader reader = XmlReader.Create(fileInfo.FullName, settings))
    {
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                var node = doc.ReadNode(reader);
                string json = JsonConvert.SerializeXmlNode(node);
            }
        }
    }
}

Пример XML, который генерирует ошибку:

<parent>
    <child>
        Text
    </child>
</parent>
<parent>
    <child>
        <grandchild>
            Text
        </grandchild>
        <grandchild>
            Text
        </grandchild>
    </child>
    <child>
        Text
    </child>
</parent>
  • 0
    Ваш пример XML не является документом XML, так как у него нет единого корневого узла. Однако это может быть фрагмент XML.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню