2013-01-23 18 views
21

Ho alcuni xmls generati automaticamente dove alcune parti di xml possono avere più righe e altre no. Il risultato è che se c'è una riga viene restituito un nodo json singolo e se ho più righe viene restituito un array con nodi json.JSON.Net La serializzazione Xml fraintende gli array

I XMLs possono assomigliare a questo

<List> 
    <Content> 
     <Row Index="0"> 
      <Title>Testing</Title> 
      <PercentComplete>0</PercentComplete> 
      <DueDate/> 
      <StartDate/> 
     </Row> 
    </Content> 
</List> 

oppure con più righe

<List> 
    <Content> 
     <Row Index="0"> 
      <Title>Update Documentation</Title> 
      <PercentComplete>0.5</PercentComplete> 
      <DueDate>2013-01-31 00:00:00</DueDate> 
      <StartDate>2013-01-01 00:00:00</StartDate> 
     </Row> 
     <Row Index="1"> 
      <Title>Write jQuery example</Title> 
      <PercentComplete>0.05</PercentComplete> 
      <DueDate>2013-06-30 00:00:00</DueDate> 
      <StartDate>2013-01-02 00:00:00</StartDate> 
     </Row> 
    </Content> 
</List> 

durante la serializzazione questi per JSON utilizzando

JsonConvert.SerializeXmlNode(xmldoc, Formatting.Indented); 

Il primo XML diventa questo

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

E secondo questa

{ 
    "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" 
      }] 
     } 
    } 
} 

Come chiaramente si vede la riga nella seconda è una matrice come dovrebbe essere, ma non sul primo. C'è qualche soluzione nota su questo tipo di problemi o ho bisogno di implementare il controllo nel mio frontend ricevendo il JSON (che sarebbe un po 'problematico dal momento che le strutture sono molto dinamiche). Il modo migliore sarebbe se ci fosse un modo per forzare json.net a restituire sempre gli array.

+0

ho trovato lo stesso problema si prega di trovare un altro soluntion se (XDocument.Parse (" 5,00 21,00 45.00 ") .Descendants (" riga "). Count()> 1) {} se (XDocument.Parse (" 1,00 5,00 45,00 6,00 10,00 65,00 11,00 100,00 98.00 ") .Descendants (" fila "). Count()> 1) { } – pratik1020

risposta

9

ho fatto correggere questo comportamento come questo

// 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); 
} 

E 'un po' sporco ma funziona. Sono ancora interessato ad altre soluzioni!

22

Da documentazione Json.NET: http://james.newtonking.com/projects/json/help/?topic=html/ConvertingJSONandXML.htm

È possibile forzare un nodo da rendere come un array aggiungendo l'attributo json:Array='true' al nodo XML si sta convertendo a JSON. Inoltre, è necessario dichiarare lo spazio dei nomi del prefisso JSON nell'intestazione XML xmlns:json='http://james.newtonking.com/projects/json' oppure si otterrà un errore XML che indica che il prefisso JSON non è dichiarato.

Il prossimo esempio è fornito dalla documentazione:

xml = @"<person xmlns:json='http://james.newtonking.com/projects/json' id='1'> 
     <name>Alan</name> 
     <url>http://www.google.com</url> 
     <role json:Array='true'>Admin</role> 
     </person>"; 

output generato:

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

Questo è interessante, grazie per la condivisione di –

+0

Come circa la stessa cosa, ma il contrario? Da json a XML e dire che si vorrebbe mantenere un array mappato su un singolo nodo xml, invece di uno per ogni elemento dell'array? – Devela

+0

json: Array = 'true' restituisce l'errore 'Prefisso spazio dei nomi' json 'non è definito' se si utilizza in un file .config. – NullVoxPopuli

7

Dare il mio +1 per Iván Pérez Gómez e fornendo un certo codice qui per sostenere la sua risposta:

Aggiungere lo spazio dei nomi json.net richiesto al nodo radice:

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 aggiungere json: attributo Array agli elementi trovati dai 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); 
      } 
     } 
    } 

Ecco un esempio di un singolo nodo figlio come un array json:

Here is a sample of a single child node as a json array:

+1

Questo sembra interessante, sicuramente qualcosa che dovrebbe essere implementato nella fonte –

+0

Accetto, sarebbe una soluzione più sicura rispetto a trattare solo un campo o un array. In Cocoa, serializer json nativo crea sempre una matrice per un singolo nodo figlio e mi sembra un approccio più coerente. –

0

mia soluzione: se JsonConvert non funziona, non usarlo. Analizza XML in dizionari/raccolte e poi in Json.Almeno in questo modo non è necessario codificare in modo rigido nessuno dei nomi degli elementi.

private JsonResult AsJsonResult(XmlDocument result) 
    { 
     var kvp = new KeyValuePair<string, object>(result.DocumentElement.Name, Value(result.DocumentElement)); 

     return Json(kvp 
      , JsonRequestBehavior.AllowGet); 
    } 

    /// <summary> 
    /// Deserializing straight from Xml produces Ugly Json, convert to Dictionaries first to strip out unwanted nesting 
    /// </summary> 
    /// <param name="node"></param> 
    /// <returns></returns> 
    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<object>(); 
       foreach (XmlNode childNode in node.ChildNodes) 
       { 
        value.Add(Value(childNode)); 
       } 
      } 
      else //regular complex element return childNodes as a dictionary 
      { 
       value = new Dictionary<string, object>(); 
       foreach (XmlNode childNode in node.ChildNodes) 
       { 
        value.Add(childNode.Name, Value(childNode)); 
       } 
      } 
     } 
     else //Simple element 
     { 
      value = node.FirstChild.InnerText; 
     } 

     return value; 
    } 
0

stesso problema Trovato utilizzando XDocument

se (XDocument.Parse ("5.0021.0045.00"). Discendenti ("riga"). Count()> 1) {}

  if (XDocument.Parse("<RUT3><row><FromKG>1.00</FromKG><ToKG>5.00</ToKG><Rate>45.00</Rate></row><row><FromKG>6.00</FromKG><ToKG>10.00</ToKG><Rate>65.00</Rate></row><row><FromKG>11.00</FromKG><ToKG>100.00</ToKG><Rate>98.00</Rate></row></RUT3>").Descendants("row").Count() > 1) 
      { 

      } 
Problemi correlati