Home > Enterprise >  How to convert JSON to XML with added custom tag in C#
How to convert JSON to XML with added custom tag in C#

Time:09-16

I have some sample JSON on this format:

{
    "id": "532-513jg-5ujkl-5jiklf",
    "externalGuid": "93804jlkfes",
    "tagNumber": "2KMA",
    "project": {
        "id": "532kg-fw13jg-553klal-5jiklf",
        "projectName": "Test",
        "projectId": "1"
    },
    "properties": [
        {
            "id": "jkl39-jkl39084-agd208-hh82a9",
            "name": "Weight",
            "value": "1000",
            "statusCode": {
                "name": "Accepted",
                "code": 1
            }
        },
        {
            "id": "jkl39-jkl384-123208-hh82a9",
            "name": "Length",
            "value": "10",
            "statusCode": {
                "name": "Not Accepted",
                "code": 3
            }
        }
    ]
}

I want to convert this to XML so I do the following: XmlDocument node = JsonConvert.DeserializeXmlNode(jsonString, "tag"); Which gives me the following XML:

<tag>
  <id>532-513jg-5ujkl-5jiklf</id>
  <externalGuid>93804jlkfes</comosUID>
  <tagNumber>2KMA</tagNumber>
  <project>
    <id>532kg-fw13jg-553klal-5jiklf</id>
    <projectName>Test</projectName>
    <projectId>1</projectId>
  </project>
  <properties>
    <id>jkl39-jkl39084-agd208-hh82a9</id>
    <name>Weight</name>
    <value>1000</value>
    <statusCode>
      <name>Accepted</name>
      <code>1</code>
    </statusCode>
  <properties>
    <id>jkl39-jkl384-123208-hh82a9</id>
    <name>Length</name>
    <value>10</value>
    <statusCode>
      <name>Not Accepted</name>
      <code>3</code>
    </statusCode>
  </properties>
</tag>

which is ALMOST what I want. However the system that is going to import the XML expects a slightly different format. It wants each of the properties to start and end with a <property> tag. So the properties array would look like this:

<properties>
  <property>
    <id>jkl39-jkl39084-agd208-hh82a9</id>
    <name>Weight</name>
    <value>1000</value>
    <statusCode>
      <name>Accepted</name>
      <code>1</code>
    </statusCode>
  </property>
  <property>
    <id>jkl39-jkl384-123208-hh82a9</id>
    <name>Length</name>
    <value>10</value>
    <statusCode>
      <name>Not Accepted</name>
      <code>3</code>
    </statusCode>
  </property>
</properties>

How can I make the XML match this template? That is, replace the properties tags with property, and wrap all the property tags in a properties parent tag.

CodePudding user response:

To change format of your input you should first deserialize JSON to matching model, then you can use System.Xml.Linq to export nodes in the order and structure whatever you desire.

Model model = JsonConvert.DeserializeObject<model>(jsonString);
var xml = new XElement("properties", 
               new XElement("property",
                  new XElement("id", model.Id),
                  new XElement("name", model.Name) /*and so on*/));

It looks like a lot of writing, but you can do whatever you want with the XML really. You can prepare methods to process model and adjust it, but it is up to your requirements.

You can also create another model with structure corresponding to that of xml file, which you will then have to fill with data from original node, but this looks like a lot of unnecessary work. I personally will use System.Xml.Linq for such task.

CodePudding user response:

Here's how I solved it. First I created C# classes for the corresponding JSON/XML elements:

    [XmlRoot("tag")]
    [JsonObject(Title = "tag")]
    public class Tag
    {
        [XmlElement("id")] public string Id { get; set; }
        [XmlElement("externalGuid")] public string ExternalGuid{ get; set; }
        [XmlElement("tagNumber")] public string TagNumber { get; set; }
        [XmlElement("project")] public Project Project { get; set; }
        [XmlArray("properties")] public List<Property> Properties { get; set; }
    }

    [XmlType("property")]
    public class Property
    {
        [XmlElement("id")] public string Id { get; set; }
        [XmlElement("name")] public string Name { get; set; }
        [XmlElement("value")] public string Value { get; set; }
        [XmlElement("status")] public Status Status { get; set; }
    }

    public class Status
    {
        [XmlElement("id")] public string Id { get; set; }
        [XmlElement("name")] public string Name { get; set; }
        [XmlElement("code")] public int Code { get; set; }
    }

    public class Project
    {
        [XmlElement("id")] public string Id { get; set; }
        [XmlElement("projectName")] public string ProjectName { get; set; }
        [XmlElement("projectId")] public string ProjectId { get; set; }
    }

Then I implemented a custom Json to XML converter:

        public static string TransformJsonToXml<T>(string json)
        {
            var jsonObjectAttribute = typeof(T).GetCustomAttribute(typeof(JsonObjectAttribute)) as JsonObjectAttribute;
            JObject root = JObject.Parse(json);
            var data = root[jsonObjectAttribute.Title];

            var obj = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(data));

            var serializer = new XmlSerializer(typeof(T));
            var stringWriter = new StringWriter();
            using var xmlWriter = XmlWriter.Create(stringWriter);
            var xmlSerializerNamespaces = new XmlSerializerNamespaces();
            xmlSerializerNamespaces.Add("","");
            serializer.Serialize(xmlWriter, obj, xmlSerializerNamespaces);
            return stringWriter.ToString();
        }

Now transforming the JSON to XML is simply: var xml = TransformJsonToXml<Tag>(jsonInput);

CodePudding user response:

You could write a custom XmlNodeConverter that would insert a parent element if the current element is an array. For example,

public class ArrayXmlNodeConverter : XmlNodeConverter
{
    public readonly string _arrayRootName;

    public ArrayXmlNodeConverter(string arrayRootName)
    {
        _arrayRootName = arrayRootName;
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JObject.Load(reader);
        ChangeArrayElementRoot(token);
        reader = token.CreateReader();
        reader.Read();
        return base.ReadJson(reader, objectType, existingValue, serializer);
    }

    private void ChangeArrayElementRoot(JToken token)
    {
         // If it is an array, insert parent element
        if (token.Type == JTokenType.Array)
        {
            var arrayHolder = new JObject { { _arrayRootName, token } };
            token.Replace(arrayHolder);
        }
        else
        {
            // check iteratively
            foreach (var childToken in token)
            {
                ChangeArrayElementRoot(childToken);
            }
        }
    }
}

Now you could use as

var xml = (XmlDocument)JsonConvert.DeserializeObject(jsonString,
                   typeof(XmlDocument), 
                   new ArrayXmlNodeConverter("tag","Property"));

Demo Code

  • Related