2013-01-23 3 views
21

У меня есть автогенерируемые xmls, где некоторые части xml могут иметь несколько строк, а некоторые - нет. В результате, если есть одна строка, возвращается один json-узел, и если у меня есть несколько строк, возвращается массив с json-узлами.JSON.Net Xml Сериализация неправильно понимает массивы

В XMLs может выглядеть так

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

Или с несколькими рядами

<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> 

При сериализации их в формате JSON с помощью

JsonConvert.SerializeXmlNode(xmldoc, Formatting.Indented); 

Первый XML становится этот

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

И второе это

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

Как ясно можно увидеть ряд на второй массив, как должно быть, но не на первый. Существует ли какое-либо известное обходное решение для такого рода проблем или мне нужно выполнить проверку в моем интерфейсе, получая JSON (это было бы немного проблематично, поскольку структуры очень динамичны). Лучше всего было бы, если бы там, где любой способ принудить json.net всегда возвращать массивы.

+0

я нашел такую ​​же проблему, пожалуйста, найти другой soluntion если (XDocument.Parse (» 5,00 21,00 45.00 ") .Descendants (" строка "). Count()> 1) { } , если (XDocument.Parse (" 1,00 5.00 45.00 6.00 10,00 65,00 11,00 100,00 98,00 ") .Descendants (" строка "). Count()> 1) { } – pratik1020

ответ

9

Я сделал исправить такое поведение, как этот

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

Это немного грязный, но это работает. Меня все еще интересуют другие решения!

22

Из Json.NET документации: http://james.newtonking.com/projects/json/help/?topic=html/ConvertingJSONandXML.htm

Вы можете заставить узел быть оказан в виде массива путем добавления атрибута json:Array='true' к узлу XML вы превращающие в JSON. Кроме того, вам нужно объявить пространство имен префиксов json в заголовке XML xmlns:json='http://james.newtonking.com/projects/json', иначе вы получите сообщение об ошибке XML, в котором указано, что префикс json не объявлен.

Следующий пример приведен в документации:

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>"; 

сгенерированным:

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

Это интересно, спасибо за обмен –

+0

Как примерно то же самое, но наоборот? От json до XML и говорим, что хотелось бы сохранить массив, сопоставленный одному узлу xml, вместо одного для каждого элемента массива? – Devela

+0

json: Array = 'true' дает ошибку 'Префикс пространства имен' json 'не определен', хотя при использовании в файле .config. – NullVoxPopuli

7

Давать мой +1 к Iván Переса Гомеса и обеспечивая некоторый код здесь, чтобы поддержать его ответ:

Добавьте искомое пространство имен json.net в корневой узел:

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

И добавить JSON: атрибут массива для элементов, найденных с помощью 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); 
      } 
     } 
    } 

Вот пример одного дочернего узла в виде массива JSON:

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

+1

Это выглядит интересно, определенно что-то, что должно быть реализовано в источнике –

+0

Согласитесь, это было бы более безопасным решением, чем иметь дело только с полем или массивом. В Cocoa встроенный сериализатор json всегда создает массив для одного дочернего узла, и он считает меня более последовательным. –

0

Мое решение: если JsonConvert не работает, не используйте его. Разбирайте XML в словари/коллекции, а затем в Json.По крайней мере, таким образом вам не нужно жестко кодировать любые имена элементов.

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

Нашли такую ​​же проблему с помощью XDocument

, если (XDocument.Parse ("5.0021.0045.00"). Потомки ("строка"). 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) 
      { 

      } 
Смежные вопросы