-Fixed converting XElements without namespace attribute to JSON

This commit is contained in:
James Newton-King
2016-04-10 11:36:21 +12:00
parent 93ba0c4b84
commit 55c87a0e73
4 changed files with 172 additions and 3 deletions
@@ -2670,6 +2670,32 @@ namespace Newtonsoft.Json.Tests.Converters
Assert.AreEqual("null", json);
}
[Test]
public void SerializeExplicitAttributeNamespace()
{
var original = XElement.Parse("<MyElement xmlns=\"http://example.com\" />");
Assert.AreEqual(@"<MyElement xmlns=""http://example.com"" />", original.ToString());
var json = JsonConvert.SerializeObject(original);
Assert.AreEqual(@"{""MyElement"":{""@xmlns"":""http://example.com""}}", json);
var deserialized = JsonConvert.DeserializeObject<XElement>(json);
Assert.AreEqual(@"<MyElement xmlns=""http://example.com"" />", deserialized.ToString());
}
[Test]
public void SerializeImplicitAttributeNamespace()
{
var original = new XElement("{http://example.com}MyElement");
Assert.AreEqual(@"<MyElement xmlns=""http://example.com"" />", original.ToString());
var json = JsonConvert.SerializeObject(original);
Assert.AreEqual(@"{""MyElement"":{""@xmlns"":""http://example.com""}}", json);
var deserialized = JsonConvert.DeserializeObject<XElement>(json);
Assert.AreEqual(@"<MyElement xmlns=""http://example.com"" />", deserialized.ToString());
}
#endif
}
}
@@ -374,6 +374,40 @@ namespace Newtonsoft.Json.Tests.Serialization
Assert.AreEqual(7, c.ExtensionData.Count);
}
[JsonObject(MemberSerialization.OptIn)]
public class MyClass
{
public int NotForJson { get; set; }
[JsonPropertyAttribute(Required = Required.Always)]
public int ForJson { get; set; }
[JsonExtensionData(ReadData = true, WriteData = true)]
public IDictionary<String, JToken> ExtraInfoJson { get; set; }
public MyClass(MyClass other = null)
{
if (other != null)
{
// copy construct
}
}
}
[Test]
public void PopulateWithExtensionData()
{
string jsonStirng = @"{ ""ForJson"" : 33 , ""extra1"" : 11, ""extra2"" : 22 }";
MyClass c = new MyClass();
JsonConvert.PopulateObject(jsonStirng, c);
Assert.AreEqual(2, c.ExtraInfoJson.Count);
Assert.AreEqual(11, (int)c.ExtraInfoJson["extra1"]);
Assert.AreEqual(22, (int)c.ExtraInfoJson["extra2"]);
}
public class MultipleExtensionDataAttributesTestClass
{
public string Name { get; set; }
@@ -528,6 +528,64 @@ namespace Newtonsoft.Json.Tests.Serialization
JObject o = (JObject)actual.Payload;
Assert.AreEqual("System.Byte[], mscorlib", (string)o["$type"]);
Assert.AreEqual("AAECAwQFBgcICQ==", (string)o["$value"]);
Assert.AreEqual(null, o.Parent);
}
[Test]
public void ReadAhead_JObject_NoParent()
{
ItemWithUntypedPayload actual = JsonConvert.DeserializeObject<ItemWithUntypedPayload>(@"{
""Payload"": {}
}",
new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
});
JObject o = (JObject)actual.Payload;
Assert.AreEqual(null, o.Parent);
}
public class ItemWithJTokens
{
public JValue Payload1 { get; set; }
public JObject Payload2 { get; set; }
public JArray Payload3 { get; set; }
}
[Test]
public void ReadAhead_TypedJValue_NoParent()
{
ItemWithJTokens actual = (ItemWithJTokens)JsonConvert.DeserializeObject(@"{
""Payload1"": 1,
""Payload2"": {},
""Payload3"": [1],
""$type"": ""Newtonsoft.Json.Tests.Serialization.MetadataPropertyHandlingTests+ItemWithJTokens, Newtonsoft.Json.Tests""
}",
new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead,
TypeNameHandling = TypeNameHandling.All
});
Assert.AreEqual(null, actual.Payload1.Parent);
Assert.AreEqual(null, actual.Payload2.Parent);
Assert.AreEqual(null, actual.Payload3.Parent);
}
[Test]
public void ReadAhead_JArray_NoParent()
{
ItemWithUntypedPayload actual = JsonConvert.DeserializeObject<ItemWithUntypedPayload>(@"{
""Payload"": [1]
}",
new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
});
JArray o = (JArray)actual.Payload;
Assert.AreEqual(null, o.Parent);
}
public class ItemWithUntypedPayload
@@ -46,7 +46,6 @@ using System.Linq;
namespace Newtonsoft.Json.Converters
{
#region XmlNodeWrappers
#if !DOTNET && !PORTABLE && !PORTABLE40
internal class XmlDocumentWrapper : XmlNodeWrapper, IXmlDocument
@@ -235,6 +234,7 @@ namespace Newtonsoft.Json.Converters
{
private readonly XmlNode _node;
private IList<IXmlNode> _childNodes;
private IList<IXmlNode> _attributes;
public XmlNodeWrapper(XmlNode node)
{
@@ -295,7 +295,14 @@ namespace Newtonsoft.Json.Converters
return null;
}
return _node.Attributes.Cast<XmlAttribute>().Select(WrapNode).ToList();
// attributes is read multiple times
// cache results to prevent multiple reads which kills perf in large documents
if (_attributes == null)
{
_attributes = _node.Attributes.Cast<XmlAttribute>().Select(WrapNode).ToList();
}
return _attributes;
}
}
@@ -327,6 +334,7 @@ namespace Newtonsoft.Json.Converters
XmlNodeWrapper xmlNodeWrapper = (XmlNodeWrapper)newChild;
_node.AppendChild(xmlNodeWrapper._node);
_childNodes = null;
_attributes = null;
return newChild;
}
@@ -861,6 +869,8 @@ namespace Newtonsoft.Json.Converters
internal class XElementWrapper : XContainerWrapper, IXmlElement
{
private IList<IXmlNode> _attributes;
private XElement Element
{
get { return (XElement)WrappedNode; }
@@ -875,11 +885,52 @@ namespace Newtonsoft.Json.Converters
{
XObjectWrapper wrapper = (XObjectWrapper)attribute;
Element.Add(wrapper.WrappedNode);
_attributes = null;
}
public override IList<IXmlNode> Attributes
{
get { return Element.Attributes().Select(a => new XAttributeWrapper(a)).Cast<IXmlNode>().ToList(); }
get
{
// attributes is read multiple times
// cache results to prevent multiple reads which kills perf in large documents
if (_attributes == null)
{
_attributes = Element.Attributes().Select(a => new XAttributeWrapper(a)).Cast<IXmlNode>().ToList();
// ensure elements created with a namespace but no namespace attribute are converted correctly
// e.g. new XElement("{http://example.com}MyElement");
string namespaceUri = NamespaceUri;
if (!string.IsNullOrEmpty(namespaceUri) && namespaceUri != ParentNode?.NamespaceUri)
{
if (string.IsNullOrEmpty(GetPrefixOfNamespace(namespaceUri)))
{
bool namespaceDeclared = false;
foreach (IXmlNode attribute in _attributes)
{
if (attribute.LocalName == "xmlns" && string.IsNullOrEmpty(attribute.NamespaceUri) && attribute.Value == namespaceUri)
{
namespaceDeclared = true;
}
}
if (!namespaceDeclared)
{
_attributes.Insert(0, new XAttributeWrapper(new XAttribute("xmlns", namespaceUri)));
}
}
}
}
return _attributes;
}
}
public override IXmlNode AppendChild(IXmlNode newChild)
{
IXmlNode result = base.AppendChild(newChild);
_attributes = null;
return result;
}
public override string Value