How to get deserialized xml attribute from dynamic object - c#

I can get the element innertext from expandoobject without any problem. I can't figure out how to get the attribute's value.
By doing Console.WriteLine(obj.Message.Body), I can get the expected string inside the body element.
private void TestXML()
{
string xmlString = #"<?xml version=""1.0"" encoding=""utf-8""?><Message important=""yes"" recevied=""2019-2-12""><Body>Hi there fella!</Body></Message>";
XDocument doc = XDocument.Parse(xmlString);
string json = JsonConvert.SerializeXNode(doc);
dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(json);
Console.WriteLine(obj.Message);
}
I did a debug and and under obj.Message I can see 3 fields:
#important with value "yes"
#received with value "2019-2-12"
Body with value "Hi there fella!"
Is there a way to retrieve the first 2 fields' values with a # prefix? I have no idea how to deal with this # character on dynamic objects.

To deal with special characters, such as "#" in dynamic object, you must cast it to `
(IDictionary). And then you can get the recevied attribute as bellow:
var received = ((IDictionary<string, object>)obj.Message)["#recevied"];

Related

Check whether JSON property contains value or array

I have a random input XML file which I am trying to convert into JSON and access values. Now JSON might have a simple JSON object or it might have array within it. When I am working with simple JSON object I am able to get values properly but when any JSON property contains an array I am getting an error that "value cannot be null". I am using newtonsoft library for this. My question is how to know whether a property is having a value or an array in JSON when every time the input file might have different structure. I hope I am able to explain my problem and the code I am using is given below:
XmlDocument Xmldoc = new XmlDocument();
Xmldoc.LoadXml(fileContents);
string root = Xmldoc.DocumentElement.Name;
var json = JsonConvert.SerializeXmlNode(Xmldoc);
var JsonStringObject = (JObject)JsonConvert.DeserializeObject(json);
var XMLNodesData = JsonStringObject[root.ToString()].Children();
List<JToken> tokens = XMLNodesData.Children().Children().ToList();
foreach (var node in tokens)
{
foreach (Field field in fields)
{
var inputString = node[field.FieldName].Value<string>();
}
}
With above code I am able to get value properly on inputString if I am having a simple XML such as :
<PatientRecords>
<Patient>
<firstname>John</firstname>
<lastname>Smith</lastname>
<patientid>111</patientid>
<dob>2022-05-13</dob>
</Patient>
<Patient>
<firstname>Martha</firstname>
<lastname>Stewart</lastname>
<patientid>112</patientid>
<dob>2022-04-14</dob>
</Patient>
</PatientRecords>
but if I Change XML to below given format then I am getting an error "Value cannot be null" incase of age property:
<PatientRecords>
<Patient>
<firstname>John</firstname>
<lastname>Smith</lastname>
<patientid>111</patientid>
<age>
<dob>2022-05-13</dob>
</age>
</Patient>
<Patient>
<firstname>Martha</firstname>
<lastname>Stewart</lastname>
<patientid>112</patientid>
<age>
<dob>2022-04-14</dob>
</age>
</Patient>
</PatientRecords>
Note: field.Fieldname is used to get tag name ("firstname", "lastname" etc) from a different XML which is required in my project. Please help regarding this and thank you in advance.
You can try below
var jsonResult = JsonConvert.DeserializeObject<dynamic>(jsonStr);
if(jsonResult.Type==JTokenType.Object)
{
//JSON contains Object
}
else if(jsonResult.Type==JTokenType.Array)
{
//JSON contains Array
}

Modifying element of Json string (in C#)

I'm trying to modify an attribute of an XML string using Json in C#. Currently I'm doing the following:
XmlDocument serializedFormXml = new XmlDocument();
serializedFormXml.LoadXml(mySerializedForm);
string formJsonString = JsonConvert.SerializeXmlNode(serializedFormXml, Newtonsoft.Json.Formatting.None, true);
JObject formJsonObj = JObject.Parse(formJsonString);
formJsonObj["#code"] = "myNewValue";
var xml = JsonConvert.DeserializeXmlNode(formJsonObj.ToString()).ToString();
When I do this I get get an exception on the last line:
Unable to cast object of type 'Newtonsoft.Json.Converters.XmlDocumentWrapper' to type 'Newtonsoft.Json.Converters.IXmlElement'
Any ideas what I'm doing wrong and how I can fix modify my form attribute "code"?
This is the XML I'm using:
<Form code="XYZ">
<Info>Data</Info>
.....
Thanks!
That's going to be way, way easier with Linq-to-XML:
var doc = XDocument.Parse(mySerializedForm);
doc.Root.SetAttributeValue(doc.Root.Name.Namespace + "code", "myNewValue");
var xml = doc.ToString();
This drops the XML declaration. If you need the XML declaration included, you can use the following extension method:
public static class XObjectExtensions
{
public static string ToXml(this XDocument xDoc)
{
using (var writer = new StringWriter())
{
xDoc.Save(writer);
return writer.ToString();
}
}
}
And then write:
var xml = doc.ToXml();
If specifically you need to make the encoding string say "UTF-8", use Utf8StringWriter from this answer.
Update
The reason you code fails is that you stripped the XML root element name away when you converted to json by passing true here:
string formJsonString = JsonConvert.SerializeXmlNode(serializedFormXml, Newtonsoft.Json.Formatting.None, true);
Thus you need to add it back when converting back:
var xml = JsonConvert.DeserializeXmlNode(formJsonObj.ToString(), serializedFormXml.DocumentElement.Name).ToString();

How to get the attribute value of xlink:title from xml

This is my xml generated for retrieving members of a group. I need to get the value tech\abc1234 from this xml.
<tcm:Trustee xlink:href="tcm:0-61-65552" xlink:type="simple" xlink:title="tech\abc1234" Type="65552" Icon="T65552L0P0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:tcm="http://www.tridion.com/ContentManager/5.0"></tcm:Trustee>
But when i try to get the attribute value like:
XElement userList = csClient.GetListXml(grpId, members);
foreach (var eachuser in userList.Elements())
{
logdetails(eachuser.Attribute("xlink:title").Value.ToString());
}
I am getting the following error :
error The ':' character, hexadecimal value 0x3A, cannot be included in a name.
Currently, you're using the string to XName conversion, which takes that string as just the local ID of an element, and that can't contain a colon.
You need to create an XName with the full namespace + local ID. Fortunately, LINQ makes that really easy:
XNamespace xlink = "http://www.w3.org/1999/xlink";
XElement userList = csClient.GetListXml(grpId, members);
foreach (var user in userList.Elements())
{
logdetails(user.Attribute(xlink + "title").Value);
}
Note that there's no need to call ToString() after Value - XAttribute.Value already returns a string.

how to exclude null from JSON? (after converting from XML)

Below is code to convert xml to json using http://json.codeplex.com/
how to exclude null from JSON? (ie "SessionId": "null")
string xml = ""; //see XML value below
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
string jsonText = JsonConvert.SerializeXmlNode(doc); //See Json value below
Xml Input
<MyResponse>
<Timestamp>2012-01-07T12:43:29</Timestamp>
<SessionId></SessionId>
</MyResponse>
Json Output
{"MyResponse":{"Timestamp":"2012-01-07T12:43:29","SessionId":null}}
You could have a simple string replace since you are outputting the JSON as a string. Do something like this:
jsonText = jsonText.Replace("null", "\"\"");
That should replace every occurrence of null with "".
It is not giving null property like this. It gives like nil to true as attribute in xml element.

Change Value of the last Attribute in an XML c#

I was working on a bunch of XMLs that all share an attribute that contains the string "name" in them. The following code selects the attribute with string "name" in it and assign a new value to it.
public void updateXmlFile(string strFileName)
{
try
{
//Load the Document
XmlDocument doc = new XmlDocument();
doc.Load(strFileName);
//Set the changed Value
string newValue = GetUniqueKey();
//Select all nodes in the XML then choose from them
//the first node that contain string "name" in it
XmlNodeList list = doc.SelectNodes("//#*");
XmlNode filteredNode = list.Cast<XmlNode>()
.First(item => item.Name.ToLower().Contains("name"));
//Assign the newValue to the value of the node
filteredNode.Value = newValue;
doc.Save(strFileName);
}
catch (XmlException xex) { Console.WriteLine(xex); }
}
Now a new XMLs were added that dosen't have the string "name" in them, so instead of modifying the attribute with string "name" in it I decided to simply modify the last attribute no matter what it was (not the first)
Can anybody tell me how to do that?
EDIT
Here is an example of my XML
<?xml version="1.0" encoding="utf-8"?>
<CO_CallSignLists Version="24" ModDttm="2010-09-13T06:45:38.873" ModUser="EUADEV\SARE100" ModuleOwner="EUADEVS06\SS2008" CreateDttm="2009-11-05T10:19:31.583" CreateUser="EUADEV\A003893">
<CoCallSignLists DataclassId="E3FC5E2D-FE84-492D-AD94-3ACCED870714" EntityId="E3FC5E2D-FE84-492D-AD94-3ACCED870714" MissionID="4CF71AB2-0D92-DE11-B5D1-000C46F3773D" BroadcastType="S" DeputyInSpecialList="1" SunSpots="1537634cb70c6d80">
<CoCallSigns EntityId="DEBF1DDB-3C92-DE11-A280-000C46F377C4" CmdID="C45F3EF1-1292-DE11-B5D1-000C46F3773D" ModuleID="6CB497F3-AD63-43F1-ACAE-2C5C3B1D7F61" ListType="HS" Name="Reda Sabassi" Broadcast="INTO" PhysicalAddress="37" IsGS="1" HCId="0" CommonGeoPos="1" GeoLat="0.0000000" GeoLong="0.0000000">
<CoRadios EntityId="E1BF1DDB-3C92-DE11-A280-000C46F377C4" RadioType="HF" />
</CoCallSigns>
</CoCallSignLists>
</CO_CallSignLists>
#Alex: You notice that the "SunSpots" attribute (last attribute in the first child element) is successfully changed. But now when I wanna load the XML back into the DB it gives me an error
Here is the modified code
public void updateXmlFile(string strFileName)
{
try
{
XDocument doc = XDocument.Load(strFileName);
XAttribute l_attr_1 = (doc.Elements().First().Elements().First().Attributes().Last());
l_attr_1.Value = GetUniqueKey();
Console.WriteLine("Name: {0} Value:{1}", l_attr_1.Name, l_attr_1.Value);
doc.Save(strFileName);
}
catch (XmlException xex) { Console.WriteLine(xex); }
}
I was thinking of making an if statment which checks if the XML has an attribute that contains string "name" in it (since most of my XMLs has an attribute that contains name in them) if it does then change the attribute's value if not look for the last attribute and change it.. not the best solution but just throwing it out there
Then definitely use Linq to XML.
Example:
using System.Xml.Linq;
string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<Commands Version=""439"" CreateUser=""Reda"">
<CmCommands DataclassId=""57067ca8-ef96-4d2e-a085-6bd7e8b24126"" OrderName = ""Tea"" Remark=""Black"">
<CmExecutions EntityId=""A9A5B0F2-6AB4-4619-9106-B0F85F86EE01"" Lock=""n"" />
</CmCommands>
</Commands>";
XDocument x = XDocument.Parse(xml);
Debug.Print(x.Elements().First().Elements().First().Attributes().Last().Value);
// Commands ^ CmCommands ^ Remark ^
That is, word for word, the last attribute of the first child of the first element.
You can also query for element/attribute names, like:
Debug.Print(x.Descendants(XName.Get("CmCommands", "")).First().Attribute(XName.Get("Remark", "")).Value);
And of course you can use all of the Linq goodness like Where, Select, Any, All etc.
Note: replace XDocument.Parse with XDocument.Load if appropriate etc.
I've not tested this but you should be able to do all of this in the XPath expression. Something like this:
//#*[contains(node-name(.), 'name')][last()]
This will return only the last attribute with the string name anywhere in its name.
If you only want the last attribute, irrespective of it's name, use this:
//#*[last()]
Look at class XmlAttributeCollection. You can get this collection by reading property Attributes of XmlNode. Just get the last by index.
Instead of .First(), use an extension method like this:
public static T LastOrDefault<T>(this IEnumerable<T> list)
{
T val = null;
foreach(T item in list)
{
val = item;
}
return val;
}

Categories

Resources