1

I have following xml string format where property key is random and not known but always start with alphanumeric character

<properties>
  <property key="EventId">3300</property>
  <property key="source">car</property>
  <property key="type">omega</property>
  <property key="a341414">any value</property>
  <property key="arandomstring_each_time_different">any value</property>
  ....
</properties>

how to achieve following format

{
   "properties":
   {
      "EventId": "3300", 
      "source": "car",
      ...
   }
}

I tried some variation of following code, but with no luck

XDocument doc = XDocument.Parse(string); 
string jsonText = JsonConvert.SerializeXNode(doc);
var dynamic = JsonConvert.DeserializeObject<ExpandoObject>(jsonText);

output

{
   "properties":{
      "property":[
         {
            "@key":"EventId",
            "#text":"3300"
         },
         {
            "@key":"source",
            "#text":"car"
         },
         ...
      ]
   }
}
2
  • 1
    Json.NET is behaving as documented in Converting between JSON and XML: Single child text nodes are a value directly against an element, otherwise they are accessed via #text. Since your <property> nodes have an attribute, the value is in a #text property. But why use Json.NET to convert from XElement to ExpandoObject? Why not do it directly like so? dotnetfiddle.net/JzCbWd. Is your XML more general than is shown in your question? Commented Jan 6, 2022 at 5:28
  • 1
    Thank you that solved my problem , my mind stuck in thinking that some library will solve problem, instead of simple solution. Commented Jan 6, 2022 at 11:50

2 Answers 2

1

Json.NET is behaving as documented in Converting between JSON and XML:

Single child text nodes are a value directly against an element, otherwise they are accessed via #text.

Since your <property> nodes have an attribute, the value is placed into in a #text property.

But why use Json.NET to convert from XElement to ExpandoObject? It's simple enough to do the conversion directly using LINQ to XML:

var doc = XDocument.Parse(xml);

IDictionary<string, object> properties = new ExpandoObject();
foreach (var property in doc.Root.Elements("property"))
    properties.Add(property.Attribute("key").Value, property.Value);
dynamic d = new ExpandoObject();
d.properties = properties;

Which results in, as required:

{
  "properties": {
    "EventId": "3300",
    "source": "car",
    "type": "omega",
    "a341414": "any value",
    "arandomstring_each_time_different": "any value"
  }
}

Demo fiddle here.

Sign up to request clarification or add additional context in comments.

Comments

1

If you want to purely rely on Json.Net then you can do that as well:

var docInXml = XDocument.Parse("...");
var docInJson = JsonConvert.SerializeXNode(docInXml);
var semiParsedJson = JObject.Parse(docInJson);
var propertyCollection = semiParsedJson["properties"]["property"] as JArray;

var keyValueMapping = new Dictionary<string, string>();
foreach(var item in propertyCollection.Children())
{
    keyValueMapping.Add((string)item["@key"], (string)item["#text"]);
}

var result = new JObject(new JProperty("properties", JObject.FromObject(keyValueMapping)));

Let's see the code line-by-line:

var docInXml = XDocument.Parse("...");

  • It parses the xml string as XDocument

var docInJson = JsonConvert.SerializeXNode(docInXml);

  • It serializes the XDocument to json
{
   "properties":{
      "property":[
         {
            "@key":"EventId",
            "#text":"3300"
         },
         {
            "@key":"source",
            "#text":"car"
         },
         {
            "@key":"type",
            "#text":"omega"
         },
         {
            "@key":"a341414",
            "#text":"any value"
         },
         {
            "@key":"arandomstring_each_time_different",
            "#text":"any value"
         }
      ]
   }
}

var semiParsedJson = JObject.Parse(docInJson);

  • It semi parses the json to be able to perform node traversal

var propertyCollection = semiParsedJson["properties"]["property"] as JArray;

  • It retrieves the property collection as an array

var keyValueMapping = new Dictionary<string, string>();

  • It defines a temporary storage for the key attributes and text values

foreach(var item in propertyCollection.Children())

  • It iterates through the array's items

keyValueMapping.Add((string)item["@key"], (string)item["#text"]);

  • It retrieves the desired fields and converts them from JObject to string
  • It stores them in the intermediate storage

JObject.FromObject(keyValueMapping)))

  • It converts the Dictionary into a JObject
{
  "EventId": "3300",
  "source": "car",
  "type": "omega",
  "a341414": "any value",
  "arandomstring_each_time_different": "any value"
}

var result = new JObject(new JProperty("properties", ...));

  • Finally, it creates a wrapper around the above created JObject
{
  "properties": {
    "EventId": "3300",
    "source": "car",
    "type": "omega",
    "a341414": "any value",
    "arandomstring_each_time_different": "any value"
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.