JSON.Net Xml Serialization interpreta mal as matrizes

Eu tenho alguns xmls autogerados onde algumas partes do xml podem ter várias linhas e outras não. O resultado é que, se houver uma linha, um único nó json será retornado e, se houver várias linhas, uma matriz com nós json será retornada.

Os xmls podem se parecer com isso

   Testing 0      

Ou com várias linhas

    Update Documentation 0.5 2013-01-31 00:00:00 2013-01-01 00:00:00   Write jQuery example 0.05 2013-06-30 00:00:00 2013-01-02 00:00:00    

Ao serializá-los para JSON usando

 JsonConvert.SerializeXmlNode(xmldoc, Formatting.Indented); 

O primeiro xml se torna isso

 { "List": { "Content": { "Row": { "@Index": "0", "Title": "Testing", "PercentComplete": "0", "DueDate": null, "StartDate": null } } } } 

E o segundo isso

 { "List": { "Content": { "Row": [{ "@Index": "0", "Title": "Update Documentation", "PercentComplete": "0.5", "DueDate": "2013-01-31 00:00:00", "StartDate": "2013-01-01 00:00:00" }, { "@Index": "1", "Title": "Write jQuery example", "PercentComplete": "0.05", "DueDate": "2013-06-30 00:00:00", "StartDate": "2013-01-02 00:00:00" }] } } } 

Como se pode ver claramente, o Row no segundo é um array como deveria ser, mas não no primeiro. Existe alguma solução conhecida neste tipo de problemas ou eu preciso implementar a verificação em meu frontend recebendo o JSON (isso seria um pouco problemático já que as estruturas são muito dinâmicas). A melhor maneira seria se houvesse alguma maneira de impor o json.net para sempre retornar matrizes.

Da documentação do Json.NET: http://james.newtonking.com/projects/json/help/?topic=html/ConvertingJSONandXML.htm

Você pode forçar um nó a ser renderizado como um Array adicionando o atributo json:Array='true' ao nó XML que você está convertendo para JSON. Além disso, você precisa declarar o namespace do prefixo json no header XML xmlns:json='http://james.newtonking.com/projects/json' ou então você receberá um erro XML informando que o prefixo json não está declarado.

O próximo exemplo é fornecido pela documentação:

 xml = @" Alan http://www.google.com Admin "; 

Saída gerada:

 { "person": { "@id": "1", "name": "Alan", "url": "http://www.google.com", "role": [ "Admin" ] } } 

Eu consertei esse comportamento assim

 // Handle JsonConvert array bug var rows = doc.SelectNodes("//Row"); if(rows.Count == 1) { var contentNode = doc.SelectSingleNode("//List/Content"); contentNode.AppendChild(doc.CreateNode("element", "Row", "")); // Convert to JSON and replace the empty element we created but keep the array declaration returnJson = JsonConvert.SerializeXmlNode(doc).Replace(",null]", "]"); } else { // Convert to JSON returnJson = JsonConvert.SerializeXmlNode(doc); } 

É um pouco sujo, mas funciona. Ainda estou interessado em outras soluções!

Dando meu +1 a Iván Pérez Gómez e fornecendo algum código aqui para apoiar sua resposta:

Adicione o namespace json.net necessário ao nó raiz:

 private static void AddJsonNetRootAttribute(XmlDocument xmlD) { XmlAttribute jsonNS = xmlD.CreateAttribute("xmlns", "json", "http://www.w3.org/2000/xmlns/"); jsonNS.Value = "http://james.newtonking.com/projects/json"; xmlD.DocumentElement.SetAttributeNode(jsonNS); } 

E para adicionar o atributo json: Array aos elementos encontrados pelo xpath:

 private static void AddJsonArrayAttributesForXPath(string xpath, XmlDocument doc) { var elements = doc.SelectNodes(xpath); foreach (var element in elements) { var el = element as XmlElement; if (el != null) { var jsonArray = doc.CreateAttribute("json", "Array", "http://james.newtonking.com/projects/json"); jsonArray.Value = "true"; el.SetAttributeNode(jsonArray); } } } 

Aqui está uma amostra de um único nó filho como uma matriz json:

Aqui está uma amostra de um único nó filho como uma matriz json:

Minha solução: se o JsonConvert não funcionar, não o use. Analise o XML em dictionarys / collections e, em seguida, no Json. Pelo menos dessa maneira você não precisa codificar qualquer um dos nomes dos elementos.

  private JsonResult AsJsonResult(XmlDocument result) { var kvp = new KeyValuePair(result.DocumentElement.Name, Value(result.DocumentElement)); return Json(kvp , JsonRequestBehavior.AllowGet); } ///  /// Deserializing straight from Xml produces Ugly Json, convert to Dictionaries first to strip out unwanted nesting ///  ///  ///  private object Value(XmlNode node) { dynamic value; //If we hit a complex element if (node.HasChildNodes && !(node.FirstChild is XmlText)) { //If we hit a collection, it will have children which are also not just text! if (node.FirstChild.HasChildNodes && !(node.FirstChild.FirstChild is XmlText)) { //want to return a list of Dictionarys for the children's nodes //Eat one level of the hierachy and return child nodes as an array value = new List(); foreach (XmlNode childNode in node.ChildNodes) { value.Add(Value(childNode)); } } else //regular complex element return childNodes as a dictionary { value = new Dictionary(); foreach (XmlNode childNode in node.ChildNodes) { value.Add(childNode.Name, Value(childNode)); } } } else //Simple element { value = node.FirstChild.InnerText; } return value; } 

Encontrei o mesmo problema usando o XDocument

if (XDocument.Parse (“5.0021.0045.00”). Descendentes (“row”). Count ()> 1) {}

  if (XDocument.Parse("1.005.0045.006.0010.0065.0011.00100.0098.00").Descendants("row").Count() > 1) { }