converting Json to XML - attributes order problem

Mar 25, 2010 at 10:28 PM

I am trying to convert from Json string to XML:

XmlDocument xmldoc = (XmlDocument)JsonConvert.DeserializeXmlNode(myjsonstring);

I am getting an error "The '@' character, hexadecimal value 0x40, cannot be included in a name." if attributes in JSON string are listed after the elements.

For example, this is a simplified version of myjson :

{"item": {"@action": "update", "@itemid": "1", "elements": [{"@action": "none", "@id": "2"},{"@action": "none", "@id": "3"}],"@description": "temp"}}

If you put @description before the "elements" it will work fine. Unfortunately for Json the order doesn't matter and it doesn't make any distinction between attributes and elements. So if in javascript I add @description to myjson after it already has "elements" it'll put it at the end. If I look at myjson in Firebug it'll show the @description with other attributes (I guess it sorts them out) but as soon as I convert it to string Object.toJSON(myjson), the @description is placed at the end.

It is strange that NewtonSoft doesn't process it correctly.

Am I doing something wrong? Any suggestions? Is there a way to sort JSON elements by name?

 

Mar 30, 2010 at 3:57 PM

So, any ideas? Can anybody help?

Oct 13, 2010 at 11:20 PM

No ideas yet... but I have the same trouble. It occurs when attributes appear after a child node... apparently the XML builder can't cope with that.

 

An example is this;

FAILS

@"{""form"":{""@id"":0,""@kind"":""heform"",""@subkind"":"""",""@legendText"":""Untitled form"",""@descriptionText"":"""",""@title"":""Test Form 1"",""@description"":"""",""@email"":"""",""@thanks"":"""",""controls"":{""control"":[{""@kind"":""subhead"",""@subkind"":"""",""@labelText"":""Your personal information"",""@descriptionText"":"""",""@required"":""no""},{""@kind"":""textfield"",""@subkind"":"""",""@text"":"""",""@labelText"":""First name"",""@descriptionText"":"""",""@maxlength"":""255"",""@required"":""yes"",""@validation"":""none"",""@validationexpression"":"""",""@errormessage"":""""}]},""@id2"":0}}";

 

 

WORKS

@"{""form"":{""@id"":0,""@kind"":""heform"",""@subkind"":"""",""@legendText"":""Untitled form"",""@descriptionText"":"""",""@title"":""Test Form 1"",""@description"":"""",""@email"":"""",""@thanks"":"""",""controls"":{""control"":[{""@kind"":""subhead"",""@subkind"":"""",""@labelText"":""Your personal information"",""@descriptionText"":"""",""@required"":""no""},{""@kind"":""textfield"",""@subkind"":"""",""@text"":"""",""@labelText"":""First name"",""@descriptionText"":"""",""@maxlength"":""255"",""@required"":""yes"",""@validation"":""none"",""@validationexpression"":"""",""@errormessage"":""""}]}}}";

 

(I just removed test ID2)

Dec 1, 2011 at 10:08 AM

Sorry to revive an old thread but this seems to be an issue still. JsonConvert.DeserializeXMLNode will fail with the above mentioned error when the JSON string contains any object with an "@"-key after a normal one.

My workaround (jquery) is to separate the object into one with all the "@"-keys and one with the others and then do an $.extend. Works, but since key-order in Javascript-objects is not guaranteed, I'm having some qualms using it in production code. 

Any progress on that?

Mar 14, 2012 at 6:53 PM
Edited Mar 14, 2012 at 7:10 PM

This is still an issue, and it's fairly clear that it's working as intended.  Newtonsoft.Json.Converters.XMLNodeConverter.ReadAttributeElements() reads the properties until it finds one that doesn't begin with "@" or "$", gathers the results and stores them in a dictionary which is returned from this method and then used to build the output.  As soon as it finds one that doesn't start with "@" or "$", it's done, so any such attributes after one that doesn't match becomes a problem.

I don't know if the JsonReader that does the initial tokenization is cloneable.  If so, I believe there is an easy fix: Clone the JsonReader and pass the clone to this method and don't stop on the first non-@/$, but read them all, storing the @/$ elements in the dictionary.  When this method returns, throw out the clone.

But I have no idea if this can be easily cloned.  That's as far as I've gone in this source.

I'd love to hear that this is fixed.  Can someone explain if there is an easy way to clone JsonReader?  I'm not really a C# person, or I'd try to chase it down further.

  -- Scott

Mar 26, 2012 at 8:06 PM

I modified XmlNodeConverter.cs to solve this problem. The only method that needs any change is ReadElement. After line 1180 which reads...

string elementPrefix = MiscellaneousUtils.GetPrefix(attributeName);

Insert the following code...

      if (propertyName.StartsWith("@"))
      {
          var attributeName = propertyName.Substring(1);
          var attributeValue = reader.Value.ToString();

          var attributePrefix = MiscellaneousUtils.GetPrefix(attributeName);

          var attribute = (!string.IsNullOrEmpty(attributePrefix))
                                   ? document.CreateAttribute(attributeName, manager.LookupNamespace(attributePrefix), attributeValue)
                                   : document.CreateAttribute(attributeName, attributeValue);

          ((IXmlElement)currentNode).SetAttributeNode(attribute);
      }
      else
      {

Then add a brace to close the else at the end of the method.

 

 

Mar 27, 2012 at 12:51 PM

I've created a bug for this issue:  http://json.codeplex.com/workitem/22446 .