diff options
author | JamesNK <james@newtonking.com> | 2010-12-27 07:00:43 +0300 |
---|---|---|
committer | JamesNK <james@newtonking.com> | 2010-12-27 07:00:43 +0300 |
commit | 87a2bbf831b1307f8763059872fa74203c60e09e (patch) | |
tree | f7d28e1cb07ba83085691b747a8ec20cac6cca7d /Src/Newtonsoft.Json | |
parent | 2c92f69c741fd5443f1b24cbd4efd45b67ea4948 (diff) |
-Added WriteArrayAttribute to XmlNodeConverter to include an attribute to help round tripping JSON and XML
-Added OmitRootObject to XmlNodeConverter to skip writing the root JSON object when converting XML
Diffstat (limited to 'Src/Newtonsoft.Json')
-rw-r--r-- | Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs | 164 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/JsonConvert.cs | 60 |
2 files changed, 180 insertions, 44 deletions
diff --git a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs index d9b9292..8c388df 100644 --- a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs +++ b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs @@ -136,6 +136,11 @@ namespace Newtonsoft.Json.Converters _element.SetAttributeNode((XmlAttribute) xmlAttributeWrapper.WrappedNode);
}
+
+ public string GetPrefixOfNamespace(string namespaceURI)
+ {
+ return _element.GetPrefixOfNamespace(namespaceURI);
+ }
}
internal class XmlDeclarationWrapper : XmlNodeWrapper, IXmlDeclaration
@@ -294,6 +299,7 @@ namespace Newtonsoft.Json.Converters internal interface IXmlElement : IXmlNode
{
void SetAttributeNode(IXmlNode attribute);
+ string GetPrefixOfNamespace(string namespaceURI);
}
internal interface IXmlNode
@@ -726,6 +732,11 @@ namespace Newtonsoft.Json.Converters {
get { return Element.Name.NamespaceName; }
}
+
+ public string GetPrefixOfNamespace(string namespaceURI)
+ {
+ return Element.GetPrefixOfNamespace(namespaceURI);
+ }
}
#endif
#endregion
@@ -749,6 +760,19 @@ namespace Newtonsoft.Json.Converters /// <value>The name of the deserialize root element.</value>
public string DeserializeRootElementName { get; set; }
+ /// <summary>
+ /// Gets or sets a flag to indicate whether to write the Json.NET array attribute.
+ /// This attribute helps preserve arrays when converting the written XML back to JSON.
+ /// </summary>
+ /// <value><c>true</c> if the array attibute is written to the XML; otherwise, <c>false</c>.</value>
+ public bool WriteArrayAttribute { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to write the root JSON object.
+ /// </summary>
+ /// <value><c>true</c> if the JSON root object is omitted; otherwise, <c>false</c>.</value>
+ public bool OmitRootObject { get; set; }
+
#region Writing
/// <summary>
/// Writes the JSON representation of the object.
@@ -763,9 +787,13 @@ namespace Newtonsoft.Json.Converters XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable());
PushParentNamespaces(node, manager);
- writer.WriteStartObject();
- SerializeNode(writer, node, manager, true);
- writer.WriteEndObject();
+ if (!OmitRootObject)
+ writer.WriteStartObject();
+
+ SerializeNode(writer, node, manager, !OmitRootObject);
+
+ if (!OmitRootObject)
+ writer.WriteEndObject();
}
private IXmlNode WrapXml(object value)
@@ -856,7 +884,16 @@ namespace Newtonsoft.Json.Converters }
}
- private void SerializeGroupedNodes(JsonWriter writer, IXmlNode node, XmlNamespaceManager manager)
+ private bool IsArray(IXmlNode node)
+ {
+ IXmlNode jsonArrayAttribute = (node.Attributes != null)
+ ? node.Attributes.SingleOrDefault(a => a.LocalName == "Array" && a.NamespaceURI == JsonNamespaceUri)
+ : null;
+
+ return (jsonArrayAttribute != null && XmlConvert.ToBoolean(jsonArrayAttribute.Value));
+ }
+
+ private void SerializeGroupedNodes(JsonWriter writer, IXmlNode node, XmlNamespaceManager manager, bool writePropertyName)
{
// group nodes together by name
Dictionary<string, List<IXmlNode>> nodesGroupedByName = new Dictionary<string, List<IXmlNode>>();
@@ -885,14 +922,7 @@ namespace Newtonsoft.Json.Converters if (groupedNodes.Count == 1)
{
- IXmlNode singleNode = groupedNodes[0];
- IXmlNode jsonArrayAttribute = (singleNode.Attributes != null)
- ? singleNode.Attributes.SingleOrDefault(a => a.LocalName == "Array" && a.NamespaceURI == JsonNamespaceUri)
- : null;
- if (jsonArrayAttribute != null)
- writeArray = XmlConvert.ToBoolean(jsonArrayAttribute.Value);
- else
- writeArray = false;
+ writeArray = IsArray(groupedNodes[0]);
}
else
{
@@ -901,12 +931,15 @@ namespace Newtonsoft.Json.Converters if (!writeArray)
{
- SerializeNode(writer, groupedNodes[0], manager, true);
+ SerializeNode(writer, groupedNodes[0], manager, writePropertyName);
}
else
{
string elementNames = nodeNameGroup.Key;
- writer.WritePropertyName(elementNames);
+
+ if (writePropertyName)
+ writer.WritePropertyName(elementNames);
+
writer.WriteStartArray();
for (int i = 0; i < groupedNodes.Count; i++)
@@ -925,47 +958,54 @@ namespace Newtonsoft.Json.Converters {
case XmlNodeType.Document:
case XmlNodeType.DocumentFragment:
- SerializeGroupedNodes(writer, node, manager);
+ SerializeGroupedNodes(writer, node, manager, writePropertyName);
break;
case XmlNodeType.Element:
- foreach (IXmlNode attribute in node.Attributes)
+ if (IsArray(node) && node.ChildNodes.All(n => n.LocalName == node.LocalName))
+ {
+ SerializeGroupedNodes(writer, node, manager, false);
+ }
+ else
{
- if (attribute.NamespaceURI == "http://www.w3.org/2000/xmlns/")
+ foreach (IXmlNode attribute in node.Attributes)
{
- string prefix = (attribute.LocalName != "xmlns")
- ? attribute.LocalName
- : string.Empty;
+ if (attribute.NamespaceURI == "http://www.w3.org/2000/xmlns/")
+ {
+ string prefix = (attribute.LocalName != "xmlns")
+ ? attribute.LocalName
+ : string.Empty;
- manager.AddNamespace(prefix, attribute.Value);
+ manager.AddNamespace(prefix, attribute.Value);
+ }
}
- }
- if (writePropertyName)
- writer.WritePropertyName(GetPropertyName(node, manager));
-
- if (ValueAttributes(node.Attributes).Count() == 0 && node.ChildNodes.Count == 1
- && node.ChildNodes[0].NodeType == XmlNodeType.Text)
- {
- // write elements with a single text child as a name value pair
- writer.WriteValue(node.ChildNodes[0].Value);
- }
- else if (node.ChildNodes.Count == 0 && CollectionUtils.IsNullOrEmpty(node.Attributes))
- {
- // empty element
- writer.WriteNull();
- }
- else
- {
- writer.WriteStartObject();
+ if (writePropertyName)
+ writer.WritePropertyName(GetPropertyName(node, manager));
- for (int i = 0; i < node.Attributes.Count; i++)
+ if (ValueAttributes(node.Attributes).Count() == 0 && node.ChildNodes.Count == 1
+ && node.ChildNodes[0].NodeType == XmlNodeType.Text)
+ {
+ // write elements with a single text child as a name value pair
+ writer.WriteValue(node.ChildNodes[0].Value);
+ }
+ else if (node.ChildNodes.Count == 0 && CollectionUtils.IsNullOrEmpty(node.Attributes))
{
- SerializeNode(writer, node.Attributes[i], manager, true);
+ // empty element
+ writer.WriteNull();
}
+ else
+ {
+ writer.WriteStartObject();
+
+ for (int i = 0; i < node.Attributes.Count; i++)
+ {
+ SerializeNode(writer, node.Attributes[i], manager, true);
+ }
- SerializeGroupedNodes(writer, node, manager);
+ SerializeGroupedNodes(writer, node, manager, true);
- writer.WriteEndObject();
+ writer.WriteEndObject();
+ }
}
break;
@@ -1116,6 +1156,7 @@ namespace Newtonsoft.Json.Converters {
if (reader.TokenType == JsonToken.StartArray)
{
+ // handle nested arrays
ReadArrayElements(reader, document, propertyName, currentNode, manager);
return;
}
@@ -1200,10 +1241,39 @@ namespace Newtonsoft.Json.Converters currentNode.AppendChild(nestedArrayElement);
+ int count = 0;
while (reader.Read() && reader.TokenType != JsonToken.EndArray)
{
DeserializeValue(reader, document, manager, propertyName, nestedArrayElement);
+ count++;
+ }
+
+ if (WriteArrayAttribute)
+ {
+ AddJsonArrayAttribute(nestedArrayElement, document);
}
+
+ if (count == 1 && WriteArrayAttribute)
+ {
+ IXmlElement arrayElement = nestedArrayElement.ChildNodes.CastValid<IXmlElement>().Single(n => n.LocalName == propertyName);
+ AddJsonArrayAttribute(arrayElement, document);
+ }
+ }
+
+ private void AddJsonArrayAttribute(IXmlElement element, IXmlDocument document)
+ {
+ element.SetAttributeNode(document.CreateAttribute("json:Array", JsonNamespaceUri, "true"));
+
+#if !NET20
+ // linq to xml doesn't automatically include prefixes via the namespace manager
+ if (element is XElementWrapper)
+ {
+ if (element.GetPrefixOfNamespace(JsonNamespaceUri) == null)
+ {
+ element.SetAttributeNode(document.CreateAttribute("xmlns:json", "http://www.w3.org/2000/xmlns/", JsonNamespaceUri));
+ }
+ }
+#endif
}
private Dictionary<string, string> ReadAttributeElements(JsonReader reader, XmlNamespaceManager manager)
@@ -1355,9 +1425,17 @@ namespace Newtonsoft.Json.Converters if (reader.TokenType == JsonToken.StartArray)
{
+ int count = 0;
while (reader.Read() && reader.TokenType != JsonToken.EndArray)
{
DeserializeValue(reader, document, manager, propertyName, currentNode);
+ count++;
+ }
+
+ if (count == 1 && WriteArrayAttribute)
+ {
+ IXmlElement arrayElement = currentNode.ChildNodes.CastValid<IXmlElement>().Single(n => n.LocalName == propertyName);
+ AddJsonArrayAttribute(arrayElement, document);
}
}
else
diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 38b56cd..f3a0978 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -786,6 +786,20 @@ namespace Newtonsoft.Json }
/// <summary>
+ /// Serializes the XML node to a JSON string.
+ /// </summary>
+ /// <param name="node">The node to serialize.</param>
+ /// <param name="formatting">Indicates how the output is formatted.</param>
+ /// <param name="omitRootObject">Omits writing the root object.</param>
+ /// <returns>A JSON string of the XmlNode.</returns>
+ public static string SerializeXmlNode(XmlNode node, Formatting formatting, bool omitRootObject)
+ {
+ XmlNodeConverter converter = new XmlNodeConverter { OmitRootObject = omitRootObject };
+
+ return SerializeObject(node, formatting, converter);
+ }
+
+ /// <summary>
/// Deserializes the XmlNode from a JSON string.
/// </summary>
/// <param name="value">The JSON string.</param>
@@ -803,8 +817,24 @@ namespace Newtonsoft.Json /// <returns>The deserialized XmlNode</returns>
public static XmlDocument DeserializeXmlNode(string value, string deserializeRootElementName)
{
+ return DeserializeXmlNode(value, deserializeRootElementName, false);
+ }
+
+ /// <summary>
+ /// Deserializes the XmlNode from a JSON string nested in a root elment.
+ /// </summary>
+ /// <param name="value">The JSON string.</param>
+ /// <param name="deserializeRootElementName">The name of the root element to append when deserializing.</param>
+ /// <param name="writeArrayAttribute">
+ /// A flag to indicate whether to write the Json.NET array attribute.
+ /// This attribute helps preserve arrays when converting the written XML back to JSON.
+ /// </param>
+ /// <returns>The deserialized XmlNode</returns>
+ public static XmlDocument DeserializeXmlNode(string value, string deserializeRootElementName, bool writeArrayAttribute)
+ {
XmlNodeConverter converter = new XmlNodeConverter();
converter.DeserializeRootElementName = deserializeRootElementName;
+ converter.WriteArrayAttribute = writeArrayAttribute;
return (XmlDocument)DeserializeObject(value, typeof(XmlDocument), converter);
}
@@ -829,7 +859,19 @@ namespace Newtonsoft.Json /// <returns>A JSON string of the XNode.</returns>
public static string SerializeXNode(XObject node, Formatting formatting)
{
- XmlNodeConverter converter = new XmlNodeConverter();
+ return SerializeXNode(node, formatting, false);
+ }
+
+ /// <summary>
+ /// Serializes the <see cref="XNode"/> to a JSON string.
+ /// </summary>
+ /// <param name="node">The node to serialize.</param>
+ /// <param name="formatting">Indicates how the output is formatted.</param>
+ /// <param name="omitRootObject">Omits writing the root object.</param>
+ /// <returns>A JSON string of the XNode.</returns>
+ public static string SerializeXNode(XObject node, Formatting formatting, bool omitRootObject)
+ {
+ XmlNodeConverter converter = new XmlNodeConverter { OmitRootObject = omitRootObject };
return SerializeObject(node, formatting, converter);
}
@@ -852,8 +894,24 @@ namespace Newtonsoft.Json /// <returns>The deserialized XNode</returns>
public static XDocument DeserializeXNode(string value, string deserializeRootElementName)
{
+ return DeserializeXNode(value, deserializeRootElementName, false);
+ }
+
+ /// <summary>
+ /// Deserializes the <see cref="XNode"/> from a JSON string nested in a root elment.
+ /// </summary>
+ /// <param name="value">The JSON string.</param>
+ /// <param name="deserializeRootElementName">The name of the root element to append when deserializing.</param>
+ /// <param name="writeArrayAttribute">
+ /// A flag to indicate whether to write the Json.NET array attribute.
+ /// This attribute helps preserve arrays when converting the written XML back to JSON.
+ /// </param>
+ /// <returns>The deserialized XNode</returns>
+ public static XDocument DeserializeXNode(string value, string deserializeRootElementName, bool writeArrayAttribute)
+ {
XmlNodeConverter converter = new XmlNodeConverter();
converter.DeserializeRootElementName = deserializeRootElementName;
+ converter.WriteArrayAttribute = writeArrayAttribute;
return (XDocument)DeserializeObject(value, typeof(XDocument), converter);
}
|