I want to make the following changes in the xml file
My test.xml
<root>
<display-name>Some Name</display-name>
....
<resource-ref id='change'>
<resource-name>Change this</resource-name>
</resource-ref>
<resource-ref id='modify'>
<resource-name>Change this too</resource-name>
</resource-ref>
</root>
I want to make changes in this xml file to look like this
<root>
<display-name>Final Name</display-name>
....
<resource-ref id='change'>
<resource-name>After Change</resource-name>
</resource-ref>
<resource-ref id='modify'>
<resource-name>After Modify</resource-name>
</resource-ref>
</root>
The first answer in this question nearly answers my question. But I need to make specific changes for elements with specific id as you can see.
This looks very simple. I tried looking answers and failed to find. Any help is appreciated.
And btw my gradle script looks like this
task ("replace")<<{
def xmlSource = file(path/to/test.xml)
def xmlDest = file(path/to/destination)
def xmlParser = new XmlParser()
xmlParser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)
def xmlRoot = xmlParser.parse(xmlSource)
xmlRoot.'display-name'[0].value = 'RTM16'
//Looking for something like this
//xmlRoot.'resource-ref'[#id='change'].'resource-name'[0].value = 'After Change'
//xmlRoot.'resource-ref'[#id='modify'].'resource-name'[0].value = 'After Modify'
def nodePrinter = new XmlNodePrinter(new PrintWriter(new FileWriter(xmlDest)))
nodePrinter.preserveWhitespace = true
nodePrinter.print(xmlRoot)
}
After going through the Node (Groovy 2.4.6) I came up with this
task ("replace")<<{
xmlSource = file(path/to/xml source file)
xmlDest = file(path/to/destinationfile)
def parser = new XmlParser()
def xmlRoot = parser.parse(xmlSource)
xmlRoot.each{
if(it.name().equals("resource-ref")&& it.#id.equals("change")){
it.'resource-name'[0].value = 'After Change'
}
else if(it.name().equals("resource-ref")&& it.#id.equals("modify")){
it.'resource-name'[0].value = 'After Modify'
}
}
def b = new XmlNodePrinter(new PrintWriter(new FileWriter(xmlDest)))
b.preserveWhitespace = true
b.print(z)
}
Not sure if its the best way. But it works
Related
I need to form such xml:
<jobs>
<job>
<title><![CDATA[cleaner]]></title>
<description><![CDATA[cleaner in af]]></description>
<text><![CDATA[cleaner weekly in af]]></text>
<referencenumber><![CDATA[518]]></referencenumber>
<company><![CDATA[we q.]]></company>
<country_code><![CDATA[NL]]></country_code>
<city><![CDATA[af]]></city>
<url><![CDATA[url]]></url>
</job>
</jobs>
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.jobs {
data.each do |data|
xml.job {
xml.title {
xml.cdata "..."
}
xml.text {
xml.cdata "..."
}
end
}
end
The above isn't working because text is an existing method on builder.
How do I create a <text>...</text> node?
From the docs:
The builder works by taking advantage of method_missing. Unfortunately some methods are defined in ruby that are difficult or dangerous to remove. You may want to create tags with the name “type”, “class”, and “id” for example. In that case, you can use an underscore to disambiguate your tag name from the method call.
Appending an underscore also works for “text”, i.e. use text_ instead:
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.job {
xml.text_ {
xml.cdata 'foo bar baz'
}
}
end
puts builder.to_xml
Output:
<?xml version="1.0" encoding="UTF-8"?>
<job>
<text><![CDATA[foo bar baz]]></text>
</job>
One can access properties file like that:
def props = new Properties()
new File('my.props').withInputStream { props.load(it) }
assert props.foo == 'bar'
I think that's quite cumbersome. Isn't there a groovier way?
// does not compile
def props = Properties.from(new File('my.props'))
assert props.foo == 'bar'
I believe the answer is no.
The doc for Groovy JDK enhancements does not contain java.util.Properties (compared to, say, java.io.File). This article implies prior art for a home-grown solution.
You can always utilize metaprogramming:
Properties.metaClass.static.from = { File f ->
def p = new Properties()
f.withInputStream { p.load(it) }
p
}
p = Properties.from(new File('a.properties'))
assert p['a'] == '10'
I'm not aware of any shortcut to create a properties from a file. I'd like to suggest a simple solution without prior variable declaration:
p = new File('my.props').withReader { reader ->
new Properties().with {
load reader
it
}
}
I've been searching and searching for a couple of days on how to do this, but I can't seem to understand sax parsing in a way that will help me accomplish what I want to accomplish. I understand sax parsing on a basic level, but I can't wrap my mind around how to use it to extract the data I need to extract.
I'm currently using:
xml data
ruby
the saxerator gem (I'm not sold on this, it's just the easiest I've found so far that I'm able to understand clearly enough)
Here's a sample of the xml structure:
<result created="2015-08-26T09:42:35-05:00" host="testdata" status="
<items>
<client>
<clientid>00001</clientid>
<name>
<![CDATA[ ABC Company ]]>
</name>
<site>
<siteid>222222</siteid>
<name>
<![CDATA[ 123 Blvd ]]>
</name>
<workstations/>
<servers>
<server>
<id>333333</id>
<name>
<![CDATA[ 123BLVD-SRV ]]>
</name>
<failed_checks>
<check>
<checkid>4444444</checkid>
<check_type>0001</check_type>
<description>
<![CDATA[Critical Events Check - Application log]]>
</description>
<dsc_247>2</dsc_247>
<date>2015-08-26</date>
<time>06:03:44</time>
<consecutive_fails>2</consecutive_fails>
<startdate>2015-08-25</startdate>
<starttime>10:43:51</starttime>
<formatted_output>
<![CDATA[Event log issues[CLIENT:]]>
</formatted_output>
<checkstatus>
<![CDATA[ Status ]]>
</checkstatus>
</check>
</failed_checks>
</server>
</servers>
</site>
</client>
What I'm trying to extract is an array of clients. Each client will have a name, a clientid, an array of its workstations (and their properties), and an array of its servers (and their properties). Something like this:
clients_array = [
{
:name => 'ABC Company',
:clientid => '00001',
:workstations => [
{
:name => 'hostname',
:id => '00002',
:failed_checks => [
{
:description => 'description', :cause => 'cause'
}
]
},
{
:name => 'hostname2',
:id => '00003',
...
}
]
},
{
:name => 'Second Company',
:clientid => '...',
...
}
]
The problem I'm running into is I can extract the client node's information easily enough, but extracting the workstation and server information for each client node is difficult.
Side note: I would just use DOM parsing, which I've done in the past with great success, but the XML I'm working with is far too large and has crashed the server.
Here's what I've been working with so far. I keep getting stuck at the site/workstations/servers nodes because sometimes there will be one site (hash element) and sometimes there are multiple sites (array element). The same goes for workstations and servers.
Since this is sax parsing, I don't understand how I can point the workstations and servers back to each client. I don't need the site data, just the workstations and servers for each client:
require 'saxerator'
def parse_sax
clients_array = []
parser = Saxerator.parser(File.new("data.xml"))
parser.for_tag(:client).each do |client|
# Create a hash to store 'this' client's data in
client_hash = Hash.new
# Grab some data
client_hash[:name] = client['name']
client_hash[:clientid] = client['clientid']
# Here's where the workstation/server code would go
parser.for_tag(:site).each do |site|
# This just goes through and finds ALL sites
end
clients_array << client_hash
end
I thought I had figured it out when I thought about parsing clients, workstations, and servers separately:
parser.for_tag(:client).each do |client|
...
end
parser.for_tag(:workstation).each do |ws|
...
end
parser.for_tag(:server).each do |srv|
...
end
But then I end up with a bunch of separate client, workstation, and server objects with no way of relating the devices back to their respective clients.
It's very possible my grasp of sax parsing is such that I'm just missing something trivial that will accomplish what I want, but I can't seem to discover the solution.
I'm more than happy to provide clarification where needed and any help is more than appreciated.
Use XMLTextReader for huge xml files. Use code like this
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
List<Client> clients = new List<Client>();
XmlTextReader reader = new XmlTextReader(FILENAME);
while (!reader.EOF)
{
if (reader.Name == "client")
{
string xmlClient = reader.ReadOuterXml();
XElement xClient = XElement.Parse(xmlClient);
Client newClient = new Client();
clients.Add(newClient);
newClient.name = xClient.Element("name").Value;
newClient.clientid = xClient.Element("clientid").Value;
newClient.workstations = xClient.Descendants("server").Select(x => new WorkStation
{
name = x.Element("name").Value,
id = x.Element("id").Value
}).ToList();
}
else
{
reader.ReadToFollowing("client");
}
}
}
}
public class Client
{
public string name { get; set;}
public string clientid { get; set; }
public List<WorkStation> workstations { get; set; }
}
public class WorkStation
{
public string name { get; set; }
public string id { get; set; }
}
}
I am trying to parse this piece of XML
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<DagUren>
<Chauffeur>Vincent</Chauffeur>
<AanmeldTijd>4 dec. 2012 09:05:42</AanmeldTijd>
<Gewerkt>04:42</Gewerkt>
</DagUren>
I created a DagUren class that contains strings with Chauffer, AanmeldTijd, Gewerkt, etc.
DagUren eenDagUren
= (from du in doc.Element("DagUren")
select new DagUren
{
Chauffeur = du.Element("Chauffeur").Value,
Gewerkt = du.Element("Gewerkt").Value,
Pauze = du.Element("Pauze").Value,
AanmeldTijd = du.Element("AanmeldTijd").Value,
}
);
The compiler respons with: Could not find an implementation of the query pattern for source type 'System.Xml.Linq.XElement'. 'Select' not found.
Please advice, i've spend quite some time on it rewriting, every guide is using a different approach...
Your problem is, that Element() does not return a collection of XElement it just returns a single object. Linq just queries collections of items, not single objects. So your solution would be:
XElement du = doc.Element("DagUren");
DagUren ennDagUren =
new DagUren
{
Chauffeur = du.Element("Chauffeur").Value,
Gewerkt = du.Element("Gewerkt").Value,
Pauze = du.Element("Pauze").Value,
AanmeldTijd = du.Element("AanmeldTijd").Value
};
Thats because Select is an extension for IEnumerable, but you are selecting single element. Make query on enumerable, and apply SingleOrDefault at the end:
DagUren eenDagUren
= (from du in doc.Elements("DagUren")
select new DagUren
{
Chauffeur = du.Element("Chauffeur").Value,
Gewerkt = du.Element("Gewerkt").Value,
Pauze = du.Element("Pauze").Value,
AanmeldTijd = du.Element("AanmeldTijd").Value,
}).SinleOrDefault();
Or simply (thus you have only one node to parse, which is root). Consider also to use nodes casting, instead of reading Value properties:
XElement root = doc.Root;
DagUren eenDagUren = new DagUren() {
Chauffeur = (string)root.Element("Chauffeur"),
Gewerkt = (TimeSpan)root.Element("Gewerkt"),
Pauze = (bool)root.Element("Pauze"), // e.g.
AanmeldTijd = (DateTime)root.Element("AanmeldTijd") });
Sorry if this is a stupid question but I am new to WP7 development (and C# in general).
I have an XML file that looks like this:
<sessions>
<session id="305">
<startdatetime>2012-09-12 09:30:00</startdatetime>
<enddatetime>2012-09-12 10:30:00</enddatetime>
<name>TEST</name>
<description>[text]</description>
<venueID>19</venueID>
<speakerID>1764,2077,2361</speakerID>
</session>
<session> ... </session>
<session> ... </session>
etc.`
</sessions>
Later in the XML there are nodes for venues and speakers, I can pull the venue out fine using
var venuedata = from query in loadedData.Descendants("venues").Descendants("venue")
where query.Attribute("id").Value == session.venueID
select new Venue
{
[stuff]
};
[etc]
But how do I do the same thing when the XML contains a comma-seperated list like the speakers in this case?
Thanks.
Use string.Split to extract the comma-separated value: http://msdn.microsoft.com/en-us/library/system.string.split.aspx
select new Venue
{
Speakers = query.Element("speakerID").Value.Split(',').ToList(),
[stuff]
};