How can I get JsonConvert.DeserializeXmlNode to serialize arrays under a single node?

StackOverflow https://stackoverflow.com/questions/22155589

  •  19-10-2022
  •  | 
  •  

Question

When I call JsonConvert.DeserializeXmlNode with a JSON string that contains an array like this:

{"Name":"Brian","Wife":{"Name":"Christine"},"Children":[{"Name":"Kiara"},{"Name":"Abrian"},{"Name":"Brooke"}]}

I get XML that looks like this:

<Root>
  <Name>Brian</Name>
  <Wife>
    <Name>Christine</Name>
  </Wife>
  <Children>
    <Name>Kiara</Name>
  </Children>
  <Children>
    <Name>Abrian</Name>
  </Children>
  <Children>
    <Name>Brooke</Name>
  </Children>
</Root>

What I want is something that looks like this:

<Root>
  <Name>Brian</Name>
  <Wife>
    <Name>Christine</Name>
  </Wife>
  <Children>
    <Child>
      <Name>Kiara</Name>
    </Child>
    <Child>
      <Name>Abrian</Name>
    </Child>
    <Child>
      <Name>Brooke</Name>
    </Child>
  </Children>
</Root>

To make this more complex, I'm trying to do this in framework code, so I don't really know what the data is that's coming in, which means that I have no idea what to call the child element, but perhaps just one problem at a time :).

Was it helpful?

Solution

The following custom XmlNodeConverter will serialize arrays under a single node, with each array element assigned a specified name. Given you don't know the incoming data, you could opt for a generic name like "Element", in which case your example json would translate to:

<Children>
   <Element>
      <Name>Christine</Name>
   </Element>
   <Element>
      <Name>Kiara</Name>
   </Element>
</Children>

The custom XmlNodeConverter:

public class SingleArrayNodeXmlConverter : XmlNodeConverter
{
    public string ArrayElementName { get; set; }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JObject.Load(reader);
        InjectArrayElements(token, ArrayElementName);
        var innerReader = token.CreateReader();
        innerReader.Read();
        return base.ReadJson(innerReader, objectType, existingValue, serializer);
    }

    private static void InjectArrayElements(JToken token, string elementName)
    {
        foreach (var childToken in token)
        {
            InjectArrayElements(childToken, elementName);
        }
        if (token.Type == JTokenType.Array)
        {
            var arrayHolder = new JObject { { elementName, token } };
            token.Replace(arrayHolder);
        }
    }
}

Usage:

var converter = new SingleArrayNodeXmlConverter
{
    DeserializeRootElementName = "Root",
    WriteArrayAttribute = false,
    ArrayElementName = "Element"
};
return (XmlDocument)JsonConvert.DeserializeObject(json, typeof(XmlDocument), converter);

This solution is obviously a compromise, but in our situation even a generic array element is better than repeating the array node itself.

OTHER TIPS

{"Name":"Brian","Wife":{"Name":"Christine"},"Children":["Child":{"Name":"Kiara"},"Child":{"Name":"Abrian"},"Child":{"Name":"Brooke"}]}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top