Как преобразовать JSON в XML с добавлением пользовательского тега в C#

#c# #json #.net #xml

Вопрос:

У меня есть несколько примеров JSON в этом формате:

 {
    "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
            }
        }
    ]
}
 

Я хочу преобразовать это в XML, поэтому я делаю следующее:
XmlDocument node = JsonConvert.DeserializeXmlNode(jsonString, "tag");
Что дает мне следующий 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>
 

а это ПОЧТИ то, чего я хочу. Однако система, которая собирается импортировать XML, ожидает немного другого формата. Он хочет, чтобы каждое из свойств начиналось и заканчивалось <property> тегом. Таким образом, массив свойств будет выглядеть следующим образом:

 <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>
 

Как я могу сделать так, чтобы XML соответствовал этому шаблону? То есть замените properties теги property и оберните все property теги в properties родительский тег.

Ответ №1:

Чтобы изменить формат ввода, вы должны сначала десериализовать JSON в соответствующую модель, затем вы можете использовать System.Xml.Linq для экспорта узлов в порядке и структуре, которые вы хотите.

 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*/));
 

Похоже, что это много написано, но на самом деле вы можете делать с XML все, что захотите. Вы можете подготовить методы для обработки модели и настроить ее, но это зависит от ваших требований.

Вы также можете создать другую модель со структурой, соответствующей структуре xml-файла, которую затем вам придется заполнить данными из исходного узла, но это выглядит как много ненужной работы. Я лично буду использовать System.Xml.Linq для такой задачи.

Ответ №2:

Вот как я это решил. Сначала я создал классы C# для соответствующих элементов JSON/XML:

     [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; }
    }
 

Затем я реализовал пользовательский конвертер Json в XML:

         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();
        }
 

Теперь преобразование JSON в XML-это просто:
var xml = TransformJsonToXml<Tag>(jsonInput);

Ответ №3:

Вы можете написать пользовательский XmlNodeConverter, который вставит родительский элемент, если текущий элемент является массивом. Например,

 public class ArrayXmlNodeConverter : XmlNodeConverter
{
    public readonly string _arrayRootName;

    public ArrayXmlNodeConverter(string rootElement,string arrayRootName)
    {
        (DeserializeRootElementName,_arrayRootName) = (rootElement,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 (token.Type == JTokenType.Array)
        {
            var arrayHolder = new JObject { { _arrayRootName, token } };
            token.Replace(arrayHolder);
        }
        else
        {
            foreach (var childToken in token)
            {
                ChangeArrayElementRoot(childToken);
            }
        }
    }
}
 

Теперь вы могли бы использовать в качестве

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

Демонстрационный код