I am trying to get the baseCode64 Data within my response data. I am getting the ReturnCode but how can I get the data inside the "Payload".
For example my response data looks like this:
<xml_response xsi:type="xsd:string"><![CDATA[<CertificateRequest><ReturnCode>0</ReturnCode><Payload content_type="application/pdf" embedded="base64">SGVsbG8gV29ybGQ=</Payload></CertificateRequest>]]></xml_response>
To get the ReturnValue I have coded this:
XElement xmlTree = XElement.Parse(response_data);
XElement returnCode = xmlTree.Element("ReturnCode");
XText returnCode_Value = returnCode.FirstNode as XText;
String b1 = returnCode_Value.Value;
Now, how can I get the Value inside the Payload which I to convert in plaintext or create a pdf.
I tried to use the same way with paylaod but i doesn't work. I am getting nothing:
XElement returnCode = xmlTree.Element("Payload");
An if I display the Elements with:
XElement xmlTree = XElement.Parse(response_data);
XElement new_data = xmlTree.Elements();
I am just getting:
0
I has been displayed the Element Payload. This is very interesting but why?
I would simply read the content of the response using the XmlSerializer, it makes it very easy to read the data into the object, and even the decoding could be done over a hidden property
So to read the certificate request, you could go for the following 2 classes
public class CertificateRequest
{
[XmlElement("ReturnCode")]
public int ReturnCode { get; set; }
[XmlElement("Payload")]
public Payload Payload { get; set; }
}
public class Payload
{
[XmlAttribute("content_type")]
public string ContentType { get; set; }
[XmlAttribute("embedded")]
public string Embedded { get; set; }
[XmlText]
public string Value { get; set; }
[XmlIgnore]
public string DecodedValue
{
get
{
if (string.IsNullOrWhiteSpace(Value))
{
return string.Empty;
}
return System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(Value));
}
}
}
and then read the string using a memorystream to deserialize it to a certificate request object, as in the following way:
class Program
{
static CertificateRequest DeserializeRequest(string content)
{
CertificateRequest request = null;
using (MemoryStream ms = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + content);
ms.Write(data, 0, data.Length);
ms.Position = 0;
XmlSerializer xs = new XmlSerializer(typeof(CertificateRequest));
request = xs.Deserialize(ms) as CertificateRequest;
}
return request;
}
static void Main(string[] args)
{
string xmlAsString = #"<CertificateRequest><ReturnCode>0</ReturnCode><Payload content_type=""application/pdf"" embedded=""base64"">SGVsbG8gV29ybGQ=</Payload></CertificateRequest>";
CertificateRequest request = DeserializeRequest(xmlAsString);
Console.WriteLine(request.Payload.Value);
Console.WriteLine(request.Payload.DecodedValue);
Console.ReadLine();
}
}
which would then print the base64 encoded value + Hello world on the second line (good one :D)
You don't show the code where you're attempting to access the value of Payload, so I'm sort of guessing what you want to do, but try this:
XElement payload = xmlTree.Element("Payload");
string b2 = payload.Value;
First off, your XML won't load because it's missing a namespace declaration for xsi. I fixed it by adding the following:
<xml_response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string" ...
Having done that, your XML contains an embedded text string literal (represented as CDATA) which is itself XML. Therefore you must:
Parse the outer XML.
Extract the character data as a string.
Parse the inner XML.
For instance:
string response_data = #"<xml_response xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xsi:type=""xsd:string""><![CDATA[<CertificateRequest><ReturnCode>0</ReturnCode><Payload content_type=""application/pdf"" embedded=""base64"">SGVsbG8gV29ybGQ=</Payload></CertificateRequest>]]></xml_response>";
var xmlTree = XElement.Parse(response_data);
var innerXml = xmlTree.Value; // Extract the string literal
var innerXmlTree = XElement.Parse(innerXml); // Parse the string literal as XML
var payload = innerXmlTree.Element("Payload").Value; // Extract the payload value
Debug.Assert(payload == "SGVsbG8gV29ybGQ="); // No assert.
Related
I have a case when I get a very big text data & each line contains some metadata + json data string.
I need to process the json data on each line.
This is what I have:
public Data GetData(string textLine)
{
var spanOfLine = textLine.AsSpan();
var indexOfComma = spanOfLine.IndexOf(":");
var dataJsonStringAsSpan = spanOfLine.Slice(indexOfComma + 1);
// now use dataJsonStringAsSpan which is ReadOnlySpan<char> to deserialize the Data
}
Where Data is a Dto class which has bunch of (7) different attributes:
public class Data
{
public int Attribute1 { get; set; }
public double Attribute2 { get; set; }
// ... more properties, emitted for the sake of brevity
}
I'm trying to achieve this with System.Text.Json API. Surprisingly it doesn't have any overload to deserialize from ReadOnlySpan<char>, so I come up with this:
public Data GetData(string textLine)
{
var spanOfLine = textLine.AsSpan();
var indexOfComma = spanOfLine.IndexOf(":");
var dataJsonStringAsSpan = spanOfLine.Slice(indexOfComma + 1);
var byteCount = Encoding.UTF8.GetByteCount(dataJsonStringAsSpan);
Span<byte> buffer = stackalloc byte[byteCount];
Encoding.UTF8.GetBytes(dataJsonStringAsSpan, buffer);
var data = JsonSerializer.Deserialize<Data>(buffer);
return data;
}
While this works, it looks very convoluted.
Is this the way to go or am I missing something more simple ?
.Net6 support Deserialize(ReadOnlySpan<Char>, show ref
Example
ReadOnlySpan<char> jsonString = "....";
WeatherForecast? weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);
Online Example
Will this work the same? Just reading your code, it looks like it will do the same thing...
public Data GetData(string textLine)
{
var split = textLine.Split(new char[] {':'});
var data = JsonSerializer.DeserializeObject<Data>(split[1]);
return data;
}
I'm trying to create an Rssfeed reader which saves info about a podcast to a JSON file and I'm having trouble serializing and deserializing to that file.
I realize that there are other threads regarding this subject, but I cannot grasp or comprehend how to apply it to my code or the reasoning behind it.
So I have a bit of code that creates a file if it doesn't exist and writes JSON data to it which looks like:
public void SaveFile(Podcast podcast)
{
try
{
JsonSerializer serializer = new JsonSerializer();
if(!File.Exists(#"C: \Users\Kasper\Desktop\Projektuppgift\Projektuppgift - Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json"))
{
string json = JsonConvert.SerializeObject( new { Podcast = podcast });
StreamWriter sw = File.CreateText(#"C:\Users\Kasper\Desktop\Projektuppgift\Projektuppgift-Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json");
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, json);
}
}
else
{
var filepath = #"C:\Users\Kasper\Desktop\Projektuppgift\Projektuppgift-Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json";
var jsonData = File.ReadAllText(filepath);
var podcasts = JsonConvert.DeserializeObject<List<Podcast>>(jsonData) ?? new List<Podcast>();
podcasts.Add(podcast);
jsonData = JsonConvert.SerializeObject(new {PodcastList = podcasts });
File.WriteAllText(filepath, jsonData);
}
}
catch (Exception ex)
{
Console.WriteLine("IO Exception ", ex.Message);
}
}
What I can't get to work is to deserialize from this file and add an object to it. Is there an easier way to add more data to the JSON file or am I missing something?
The Podcast class looks like this:
public class Podcast
{
public string url { get; set; }
public string name { get; set; }
public int updateInterval { get; set; }
public string category { get; set; }
//public Category category = new Category();
public List<Episode> episodes { get; set; }
public Podcast(string url, string name, Category category, List<Episode> episodes, int updateInterval)
{
this.url = url;
this.name = name;
this.category = category.name;
this.episodes = episodes;
this.updateInterval = updateInterval;
}
public Podcast(Podcast p)
{
this.url = p.url;
this.name = p.name;
this.category = p.category;
this.episodes = p.episodes;
this.updateInterval = p.updateInterval;
}
}
There appear to be a couple of issues here:
You are checking for the existence of a different file than the one you are reading/writing. The former filename has extra spaces in it. The best way to avoid this problem is to use a variable to contain the filename rather than hardcoding it in three separate places.
You are inconsistent about the JSON format you are writing and reading:
When you first create the file (in the first branch), you are writing a JSON object that contains a property Podcast which then contains a single podcast.
When you attempt to read the JSON file, you are treating the entire JSON as a list of podcasts.
After tacking the new podcast onto the list, you are writing the JSON as a single object containing a PodcastList property, which then contains the list.
You need to use a consistent JSON format. I would recommend breaking your code into smaller methods to read and write the podcasts.json file like this so that it is easier to reason about:
public static List<Podcast> ReadPodcastsFromFile(string filepath)
{
if (!File.Exists(filepath)) return new List<Podcast>();
string json = File.ReadAllText(filepath);
return JsonConvert.DeserializeObject<List<Podcast>>(json);
}
public static void WritePodcastsToFile(List<Podcast> podcasts, string filepath)
{
string json = JsonConvert.SerializeObject(podcasts);
// This will overwrite the file if it exists, or create a new one if it doesn't
File.WriteAllText(filepath, json);
}
Then, you can simplify your SaveFile method down to this (I would be tempted to rename it to SavePodcast):
public void SaveFile(Podcast podcast)
{
var filepath = #"C:\Users\Kasper\Desktop\Projektuppgift\Projektuppgift-Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json";
List<Podcast> podcasts = ReadPodcastsFromFile(filepath);
podcasts.Add(podcast);
WritePodcastsToFile(podcasts, filepath);
}
Notice I've also removed the exception handling from SaveFile. You should move that up to wherever SaveFile is called, so that you can take appropriate action at that point if an exception is thrown, e.g.:
try
{
SaveFile(podcast);
}
catch (Exception ex)
{
// Show a message to the user indicating that the file did not save
}
I'm just still learning c# but it might be that you deserialise into a list of podcasts and when you serialise you're serliasing into an object type.
My webapi method is:
public JsonResult<List<MyClass>> PullData()
{
List<MyClass> data = new List<MyClass>();
data = db.TableName.Select(x => new MyClass
{
Id = x.Id,
IsActive = x.IsActive,
//other attribute..
}).ToList();
return Json(data);
}
And I'm consuming this webapi as:
public async Task<string> Index()
{
string apiUrl = "http://localhost:90/api/Scheduler/pulldata";
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(apiUrl);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
JsonConvert.DeserializeXmlNode(data, "root"); //exception: XmlNodeConverter can only convert JSON that begins with an object.
}
}
return "Error";
}
I get error:
XmlNodeConverter can only convert JSON that begins with an object.
Also In api consumption method (i.e, Index) when I debug and see data in var data = await response.Content.ReadAsStringAsync(); as JSON Visualizer it shows data fine.
But when I do XML visualizer, it doesn't show data.
Update:
The data is too large. I can't share it. Here is the screen shot of data.
Update 2:
Here is a part of json data from begining:
[{"LearningActivityKey":2122,"ModuleName":"certName","ModuleVersion":1.0,"ModuleDescription":"<p><span style=\"background-color:rgb(240, 240, 240); font-family:archivo narrow,helvetica,arial,sans-serif; font-size:16px; line-height:20px; white-space:pre-line\">Learn SAP
Update 3:
I have changed the webapi method PullData() to send only two records, so that we can easily visualize wethere the issue is with json data.
Complete data is:
[{"LearningActivityKey":2122,"ModuleName":"certName","ModuleVersion":0.0,"ModuleDescription":null,"BadgeName":null,"BadgeVersion":null,"BadgeDescription":null,"MozillaBadge":null,"LearningActivityName":null,"LearningActivityDescription":null,"StepName":null,"StepVersion":null,"StepDescription":null,"IsActive":false,"IsPublished":false,"CreatedDate":"0001-01-01T00:00:00","ModifiedDate":null},{"LearningActivityKey":2122,"ModuleName":"certName","ModuleVersion":0.0,"ModuleDescription":null,"BadgeName":null,"BadgeVersion":null,"BadgeDescription":null,"MozillaBadge":null,"LearningActivityName":null,"LearningActivityDescription":null,"StepName":null,"StepVersion":null,"StepDescription":null,"IsActive":false,"IsPublished":false,"CreatedDate":"0001-01-01T00:00:00","ModifiedDate":null}]
I pasted data in https://jsonformatter.curiousconcept.com/ and it says:
And XML Visualizer still doesn't show any data.
The exception is self-explanatory: you cannot convert JSON to XML unless the root token is an object, even if you use the JsonConvertDeserializeXmlNode(String, String) method to specify an outer root element name.
As to why this is true, the documentation page Converting between JSON and XML shows that a JSON array is converted to a repeating sequence of XML elements without an added outer container element. I.e. JSON like this (simplified from the documentation):
{
"root": {
"person": [
{
"name": "Alan"
},
{
"name": "Louis"
}
]
}
}
Is converted to XML as follows:
<root>
<person>
<name>Alan</name>
</person>
<person>
<name>Louis</name>
</person>
</root>
Notice that an outer <root> node is created, and a repeated sequence of <person> nodes -- but nothing in between? If there were no outer object with a "root" property in the JSON then Json.NET would have tried to create XML with multiple <person> root elements. This is disallowed by the XML standard which requires exactly one root element. Thus it appears a JSON array must be contained within at least two levels of JSON object nesting to be successfully converted to XML (although one of those levels could come by specifying an outer root element name via JsonConvertDeserializeXmlNode(String, String)).
As a workaround, you can introduce the following extension methods to nest your JSON in an extra level of object.
First, grab ChainedTextReader and public static TextReader Extensions.Concat(this TextReader first, TextReader second) from the answer to How to string multiple TextReaders together? by Rex M. Using them, create the following extension methods:
public static partial class JsonExtensions
{
public static XmlDocument DeserializeXmlNode(string json, string rootName, string rootPropertyName)
{
return DeserializeXmlNode(new StringReader(json), rootName, rootPropertyName);
}
public static XmlDocument DeserializeXmlNode(TextReader textReader, string rootName, string rootPropertyName)
{
var prefix = "{" + JsonConvert.SerializeObject(rootPropertyName) + ":";
var postfix = "}";
using (var combinedReader = new StringReader(prefix).Concat(textReader).Concat(new StringReader(postfix)))
{
var settings = new JsonSerializerSettings
{
Converters = { new Newtonsoft.Json.Converters.XmlNodeConverter() { DeserializeRootElementName = rootName} },
DateParseHandling = DateParseHandling.None,
};
using (var jsonReader = new JsonTextReader(combinedReader) { CloseInput = false, DateParseHandling = DateParseHandling.None })
{
return JsonSerializer.CreateDefault(settings).Deserialize<XmlDocument>(jsonReader);
}
}
}
}
// Taken from
// https://stackoverflow.com/questions/2925652/how-to-string-multiple-textreaders-together/2925722#2925722
public static class Extensions
{
public static TextReader Concat(this TextReader first, TextReader second)
{
return new ChainedTextReader(first, second);
}
private class ChainedTextReader : TextReader
{
private TextReader first;
private TextReader second;
private bool readFirst = true;
public ChainedTextReader(TextReader first, TextReader second)
{
this.first = first;
this.second = second;
}
public override int Peek()
{
if (readFirst)
{
return first.Peek();
}
else
{
return second.Peek();
}
}
public override int Read()
{
if (readFirst)
{
int value = first.Read();
if (value == -1)
{
readFirst = false;
}
else
{
return value;
}
}
return second.Read();
}
public override void Close()
{
first.Close();
second.Close();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
first.Dispose();
second.Dispose();
}
}
}
}
And convert to XML as follows:
var doc = JsonExtensions.DeserializeXmlNode(data, "root", "array");
Using the JSON from your question, the following XML is generated:
<root>
<array>
<LearningActivityKey>2122</LearningActivityKey>
<ModuleName>certName</ModuleName>
<!-- Additional properties omitted -->
</array>
<array>
<LearningActivityKey>2122</LearningActivityKey>
<ModuleName>certName</ModuleName>
<!-- Additional properties omitted -->
</array>
</root>
Working sample .Net fiddle.
I want to covert to string into object with value. I mean let's say i have string that has XML code inside like:
Code Snippet:
String response =#"<?xml version=""1.0"" encoding=""utf-8""?>\r\n
<Request>\r\n
<TransactionType>ADMIN</TransactionType>\r\n
<Username>abc</Username>\r\n
<Password>def</Password>\r\n
</Request>";
I have a Class that has all the properties which mentioned in Xml like
Class ResponseClass
String UserName;
String Password;
String Transaction;
How can I set all the values in ResponseClass object without string parsing?
I have tried it with serialization but it gives me some problem in windows 8.1 app store project due to limitation in API.
Is there any way to get it sorted?
Thanks
Here's a very simple way using XDocument.Parse(String) from System.Xml.Linq:
String response = #"<?xml version=""1.0"" encoding=""utf-8""?>
<Request>
<TransactionType>ADMIN</TransactionType>
<Username>abc</Username>
<Password>def</Password>
</Request>";
var xml = XDocument.Parse(response);
var request = xml.Element("Request");
var responseObject = new ResponseClass()
{
UserName = request.Element("Username").Value,
Password = request.Element("Password").Value,
Transaction = request.Element("TransactionType").Value,
};
Or, if the Windows store apps support it, you can use the built in XmlSerializer (if not, you can just ignore this bit). Just define your class using the XmlRoot and XmlElement attributes like this:
[XmlRoot("Request")]
public class ResponseClass
{
[XmlElement("Username")]
public String UserName { get; set; }
[XmlElement("Password")]
public String Password { get; set; }
[XmlElement("TransactionType")]
public String Transaction { get; set; }
}
and then use the create an XmlSerializer and StringReader to deserialize it:
String response = #"<?xml version=""1.0"" encoding=""utf-8""?>
<Request>
<TransactionType>ADMIN</TransactionType>
<Username>abc</Username>
<Password>def</Password>
</Request>";
var serializer = new XmlSerializer(typeof(ResponseClass));
ResponseClass responseObject;
using (var reader = new StringReader(response))
{
responseObject = serializer.Deserialize(reader) as ResponseClass;
}
Unless the Xml was generated via some serializer you will have to code it manually to match. Serialization and deserialization go hand in hand. Therefore if your xml file is the product of a serializer then there would most likely be a deserializer. If you have the option of re-creating this xml file then do so using an XmlSerializer, that way you can deserialize.
If you don't want to use XmlSerializer thenThis is bit ugly but hope this will help with what you required. I have implemented something like that for some of my use. First make an extension method.
public static class TextUtil
{
public static string JustAfter(this string Str, string Seq, string SeqEnd)
{
string Orgi = Str;
try
{
int i = Str.IndexOf(Seq);
if (i < 0)
return null;
i = i + Seq.Length;
int j = Str.IndexOf(SeqEnd, i);
int end;
if (j > 0) end = j - i;
else end = Str.Length - i;
return Orgi.Substring(i, end);
}
catch (Exception)
{
return String.Empty;
}
}
}
Now you can use it as shown.
private void ParseXml(string responce) // respnce is xml string.
{
string transactionType = responce.JustAfter("<TransactionType>", "</TransactionType>");
string userName = responce.JustAfter("<Username>", "</UserName>");
string password = responce.JustAfter("<Password>", "</Password>");
ResponceClass resClass = new RespnceClass()
{
Transaction = transactionType,
UserName = userName,
Password = password
});
}
I've been landed with a feed of XML data that I need to deserialise into objects in a Silverlight (v5) application. The data looks like:
<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>
I've created a class at the Silverlight side, and I'm trying to get this XML - which, you'll notice, is missing a declaration and a namespace - into objects.
StringReader sr = null;
string data = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
sr = new StringReader(data);
XmlSerializer xs = new XmlSerializer(typeof (AgentState));
AgentState agent = (AgentState) xs.Deserialize(sr);
.. but this throws an error an error in xml document (1,2), as it's missing the declaration. Even manually adding a dummy declaration gives further errors about missing namespaces.
I've found other questions about ignoring namespace/declarations in XML, but none of these seem to work in Silverlight.
Can anyone advise on the best way to get this XML deserialised into an object?
This seems to work:
public class AgentState
{
public string agentName { get; set; }
public string extension { get; set; }
public string currentlyIn { get; set; }
}
static void Main(string[] args)
{
var s = #"<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>";
XmlSerializer serializer = new XmlSerializer(typeof(AgentState));
var ms = new MemoryStream(Encoding.UTF8.GetBytes(s));
var obj = serializer.Deserialize(ms);
}
I'm wondering what issue you have with appending the xml declaration to the string. This appears to work ok:
[System.Xml.Serialization.XmlRootAttribute("AgentState")]
public class AgentState
{
public string agentName {get; set;}
public int extension {get; set;}
public string currentlyIn {get; set;}
}
public void RunSerializer()
{
System.Xml.Serialization.XmlSerializer agent_serializer =
new System.Xml.Serialization.XmlSerializer(typeof(AgentState));
string agent_state_text = File.ReadAllText(#"C:\Temp\AgentState.xml");
Console.WriteLine(agent_state_text + Environment.NewLine);
string xml_agent_state = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + agent_state_text;
Console.WriteLine(xml_agent_state + Environment.NewLine);
AgentState agent_state = new AgentState();
using(StringReader tx_reader = new StringReader(xml_agent_state))
{
if (tx_reader != null)
{
agent_state = (AgentState)agent_serializer.Deserialize(tx_reader);
}
}
Console.WriteLine(agent_state.agentName);
Console.WriteLine(agent_state.extension);
Console.WriteLine(agent_state.currentlyIn);
}
Output:
<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>
<?xml version="1.0" encoding="UTF-8"?>
<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>
jbloggs
12345
TestStatus
I've managed to get it working using the following code - I'm not convinced it's the "right" way to do things, but it seems to work:
string data = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
var document = XDocument.Parse(data);
AgentState agent= (from c in document.Elements()
select new AgentState()
{
agentName = c.Element("agentName").Value,
extension = c.Element("extension").Value,
currentlyIn=c.Element("currentlyIn").Value
}).Single();
Thanks for the advice, it got me on the right track.