I've got this Groovy code working to delete nodes using xpath strings, but I'm having problems deleting nodes where the xpath results in multiple node instances.
Sample XML...
<root>
<element1>foo</element1>
<element2>bar</element2>
<items>
<item>
<name>a</name>
<desc>b</desc>
<item>
<item>
<name>c</name>
<desc>x</desc>
</item>
</items>
</root>
Code to delete nodes...
def resource = XmlSlurper().parseText(xml)
def xpathsToDelete = ['/root/element1','/root/items/item/name']
xpathsToDelete.each {
def pathTokens = it.path.tokenize '/'
def currentNode = resource
if ( currentNode.name() == pathTokens.first() ) {
def xpath = pathTokens.tail().join '/'
currentNode = currentNode."${xpath}"
currentNode.replaceNode{}
}
}
The above code removes the node element1 using xpath /root/element1, which evaluates to a single node, but does not work for /root/items/name which evaluates to multiple nodes.
This is a tricky one. It is related to this question, which is vital to my answer.
Here is a solution:
import groovy.util.*
import groovy.xml.*
def xml = """<root>
<element1>foo</element1>
<element2>bar</element2>
<items>
<item>
<name>a</name>
<desc>b</desc>
</item>
<item>
<name>c</name>
<desc>x</desc>
</item>
</items>
</root>"""
def removeNodes = { doc, path ->
def nodes = doc
path.split("\\.").each { nodes = nodes."${it}" }
nodes.each { it.replaceNode{} }
}
def resource = new XmlSlurper().parseText(xml)
def xpathsToDelete = ['/root/element1','/root/items/item/name']
xpathsToDelete.each { xpath ->
def trimXPath = xpath.replaceFirst( "/root/", "").replace("/",".")
removeNodes(resource, trimXPath)
}
println XmlUtil.serialize(new StreamingMarkupBuilder().bind {
mkp.yield resource
})
This seems to work as well:
import groovy.xml.*
def xml = '''<root>
| <element1>foo</element1>
| <element2>bar</element2>
| <items>
| <item>
| <name>a</name>
| <desc>b</desc>
| </item>
| <item>
| <name>c</name>
| <desc>x</desc>
| </item>
| </items>
|</root>'''.stripMargin()
def newxml = new XmlSlurper().parseText( xml ).with { x ->
[ '/root/element1', '/root/items/item/name' ].each { path ->
def s = path.split( '/' ).drop( 2 ).inject( x ) { element, p ->
element."$p"
}?.replaceNode {}
}
x
}
println XmlUtil.serialize(new StreamingMarkupBuilder().bind {
mkp.yield newxml
})
Related
I am using graphene in python.
Let's say I have the following schema:
extends type Query {
a(search:String):A
}
type A {
b:B
important_info:ID
}
type B {
fieldone: String
fieldtwo: String
}
Now I'd like to query:
query {
a(search:"search string") {
b {
fieldone
}
}
}
however fieldone is based on important_info.
My class B looks like this:
class B(graphene.ObjectType):
fieldone = graphene.String()
fieldtwo = graphene.String()
def resolve_fieldone(self,info):
# Here I want access to important_info, but I don't know how ...
return "something based on important_info"
How can I access important info from within the resolver of fieldone?
It seems there is no obvious or documented solution for this requirement.
I solved it by adding the root object to info.context within the outermost type:
class B(ObjectType):
c = String()
def resolve_c(parent, info):
return 'foo' if info.context['z'] == '' else 'bar'
class A(ObjectType):
b = Field(B)
def resolve_b(parent, info):
return parent.b
class Query(ObjectType):
a = Field(A)
z = String()
def resolve_a(parent, info):
return some_function_to_get_a()
def resolve_z(parent, info):
z = some_function_to_get_z()
info.context['z'] = z
return z
I am using this code for sequence generation
class cardInfo(models.Model):
_name = "library.card"
card_number = fields.Char(String = "Card Number" , size = 7, Translate = True, readonly = True)
user_name = fields.Many2one('student.student',String = "Name")
card_type = fields.Selection([('s', 'Student'), ('l', 'Staff')] , String = "Card Type")
number_of_book_limit = fields.Integer(String = "No Of Book Limit" , default = 0)
#api.model
def create(self, vals):
seq = self.env['ir.sequence'].next_by_code('library.card.number') or '/'
vals['card_number'] = seq
return super(cardInfo, self).create(vals)
but i am getting only the '/' as sequence number.. why?
You need to create "ir.sequance" in xml file like,
<record id="seq_library_card" model="ir.sequence">
<field name="name">Library Card</field>
<field name="code">library.card</field>
<field name="prefix">LIB</field>
<field name="padding">5</field>
<field name="company_id" eval="False" />
</record>
In Py file you have to write like,
#api.model
def create(self, vals):
x = self.env['ir.sequence'].next_by_code('library.card') or '/'
vals['card_number'] = x
return super(LibraryCard, self).create(vals)
This is the first time that I created a XML document using LINQToXML.
I am trying to understand how can I conditionally create attributes(or elements) when creating my document?
In this example a given car may/may not have a feature to it, so in that case I would not want to create that element, I also may have certain attributes in the feature node that could be missing. How could I handle these scenarios?
XDocument xDoc = new XDocument(
new XElement("root",
new XElement("NodeA"),
new XElement("Cars",
from p in listCars
select new XElement("Car", new XAttribute("name", p.CarName),
new XElement("Feature", new XAttribute("door", p.Door), new XAttribute("model", p.Model))
)
)
)
);
Desired result #1 (All features are missing for a given car):
<root>
<NodeA />
<Cars>
<Car name="Honda">
<Feature door="4" model="Accord" />
</Car>
<Car name="Ford" />
</Cars>
</root>
Desired result #2 (Some features could exist)
<root>
<NodeA />
<Cars>
<Car name="Honda">
<Feature door="4" model="Accord" />
</Car>
<Car name="Ford">
<Feature model="Focus" />
</Car>
</Cars>
</root>
2 seperate solutions in here. Either use a method to create the features node, or do it all in one:
static void Main(string[] args)
{
var listCars = new List<Car>();
listCars.Add(new Car { CarName = "test 1", Door = "0", Model = "" });
listCars.Add(new Car { CarName = "test 2", Door = "", Model = "" });
listCars.Add(new Car { CarName = "test 3", Door = "0", Model = "0" });
XDocument xDoc2 = new XDocument(
new XElement("root",
new XElement("NodeA"),
new XElement("Cars",
from p in listCars
select new XElement("Car",
new XAttribute("name", p.CarName),
p.Door != "" || p.Model != "" ?
new XElement("Feature",
p.Door != "" ? new XAttribute("door", p.Door) : null,
p.Model != "" ? new XAttribute("model", p.Model) : null) : null
)
)
)
);
XDocument xDoc = new XDocument(
new XElement("root",
new XElement("NodeA"),
new XElement("Cars",
from p in listCars
select new XElement("Car",
new XAttribute("name", p.CarName),
CreateFeature(p)
)
)
)
);
}
static XElement CreateFeature(Car p)
{
var el = new XElement("Feature",
p.Door != "" ? new XAttribute("door", p.Door) : null,
p.Model != "" ? new XAttribute("model", p.Model) : null);
return !el.Attributes().Any() ? null : el;
}
If you supply null instead of an element, it will be ignored, so you can use constructs like the following.
p.CarName != null ? new XAttribute("name", p.CarName) : null
If you're using C# 6, you can use null propagation.
How would I go about creating my desired list by using LINQToXml?
I am close with both attempts so far. I should be able to do this without creating to separate queries right?
This is my XML:
<main>
<cars>
<car name="Honda">
<feature door="4" name="Accord" />
<feature door="2" name="Civic"/>
<feature door="4" name="CRV"/>
</car>
<car name="Ford"/>
<car name="Kia"/>
<car name="Subaru">
<feature door="4" name="Outback"/>
<feature door="4" name="Legacy"/>
</car>
</cars>
</main>
Attempt #1
This will return the first car with feature.
var listCars = (from c in doc.Root.Descendants("cars")
select new Car
{
Model = (p.Element("car") != null) ? p.Element("car").Attribute("name").Value : null,
Door = (p.Element("car") != null) ? p.Element("car").Attribute("door").Value : null,
Name = p.Attribute("name").Value
}).ToList();
Attempt #2
This will return all of the cars that have features
Ford and Kia would be missing
var cars = from c in doc.Root.Descendants("cars")
select c;
var listPermissions = (from c in cars.Descendants("car")
let cName = p.Parent.Attribute("name").Value
select new Car
{
Model = p.Attribute("name").Value,
Door = p.Attribute("door").Value,
Name = pgn
}).ToList();
What I am trying to do is to create a list of cars that look like:
Honda, 4, Accord
Honda, 2, Civic
Honda, 4, CRV
Ford, null, null
Kia, null, null
Subaru, 4, Outback
Subaru, 4, Legacy
You can do this by using a LEFT JOIN in LINQ as outlined on 101 LINQ Samples.
var makes =
(from doc in document.Root.Descendants("cars").Descendants("car")
join f in document.Root.Descendants("cars").Descendants("car").Descendants("feature")
on doc.Attribute("name").Value.ToLowerInvariant() equals f.Parent.Attribute("name").Value.ToLowerInvariant() into ps
from f in ps.DefaultIfEmpty()
select new
{
Model = doc.Attribute("name").Value,
Door = f == null ? string.Empty : f.Attribute("door").Value,
Name = f == null ? string.Empty : f.Attribute("name").Value
})
.ToList();
I have a rss feed which is below format.
<response>
<results>
<game>
<image>
<icon_url>
<![CDATA[
http://-------------------
]]>
</icon_url>
<medium_url>
<![CDATA[
http://----
]]>
</medium_url>
</image>
</game>
</results>
</response>
Now, I want to retrieve the medium_url using LINQ.
var items = from item in rssFeed.
Elements("response").Elements("results").Elements("game")
select new
{
Image1 = item.Element("image").Element("medium_url").value
}
This doesn't seem to work.
You just need to make a small change:
var items = from item in rssFeed.
Elements("response").Elements("results").Elements("game")
select new
{
Image1 = (string)item.Element("image").Element("medium_url")
};
or
var items = from item in rssFeed.
Elements("response").Elements("results").Elements("game")
select new
{
Image1 = item.Element("image").Element("medium_url").Value
};